Commit 2 deleted the GL rendering backend's implementations; this step removes the package references and shader vocabulary they leave behind, so nothing in the App project still spells Silk.NET.OpenGL. Silk.NET.OpenGL and Silk.NET.OpenGL.Extensions.ARB are dropped from AcDream.App.csproj. Chorizite.Core stays — the audit is NOT clean: its Render.Enums (TextureFormat, BufferUsage) and Lib.BoundingBox types are used directly and extensively across the Wb texture/mesh pipeline, independent of the deleted GL IUniformBuffer implementers the package comment used to cite. The stale comment is corrected in place. IMeshPipelineDevice.Gl is removed along with the GL? gl parameter threaded through WbMeshAdapter's four constructors, WorldRenderComposition's CreateMeshAdapter, and VulkanMeshPipelineDevice's Gl => null implementation — nothing read any of them once the legacy per-mesh upload bodies were gone (confirmed by grep: the sole non-doc-comment hit was a test assertion). While in WbMeshAdapter.Dispose(), found and fixed a real bug along the way: its teardown still pattern-matched the deleted GL GpuFrameFlightController to decide whether to wait for submitted work, which VulkanFrameFlightController replaced at slice V6a without this site being updated — so the wait had been silently dead on every Vulkan run since then. Retargeted to VulkanFrameFlightController, which carries the same WaitForSubmittedWork(). The GL pixel-format vocabulary (Silk.NET.OpenGL.PixelFormat/PixelType) that WorldTextureArray/TextureFormatExtensions/TextureAtlasManager used for upload validation is replaced by AcDream.Content's existing Silk.NET-free UploadPixelFormat/UploadPixelType enums (added at MP1a to keep the bake tool GL-free); two new members (Rgb, Red, Float) extend that enum with their GL ABI constants to cover the full vocabulary WorldTextureArray needs, since MP1a's original set only covered what the extractor itself emits. ObjectMeshManager's App-boundary cast `(Silk.NET.OpenGL.PixelFormat?)batch.UploadPixelFormat` becomes a direct pass-through now that both sides share the type. GpuBindingModel.StorageTextureTable (the GL-only binding=9 emulation of the Vulkan texture table) is deleted and StorageBindingCount drops from 10 to 9; the descriptor-set-layout code that builds from that count (VulkanPipelineLayouts, VulkanFrameBindings) is untouched and just allocates one fewer always-dummy-seeded, always-unused binding. Several fully dead GL-only classes came along for the ride, confirmed by zero construction sites: SilkFramebufferViewportTarget (NullFramebufferViewportTarget is the sole production IFramebufferViewportTarget), SilkRenderGlStateReader (NullRenderGlStateReader.Instance is the sole IRenderGlStateReader), RuntimeRenderFrameClearPhase (VulkanRenderFrameClearPhase is the sole IRenderFrameClearPhase, expressing the same atmosphere-clear logic as a pass load-op instead), and GpuFrameTimer plus FrameProfiler's GL-owning FrameBoundary(GL) overload and BeginGpuFrame/EndGpuFrame bracket (RecordGpuSample is the only GPU-timing path any backend uses now — the ACDREAM_WB_DIAG nested-query exclusion these existed for no longer applies, since WbDrawDispatcher's own diagnostic GPU sampling already moved to the device's Vulkan timer pool). GpuFrameFlightController itself stays (never constructed with a real fence API in production, but its retirement-ledger/serial-ring logic is backend-neutral and still covered by its own unit tests) — only its GL-specific parts (the public GL constructor overload, SilkGpuFenceApi) are deleted, since removing the whole class would mean restructuring the frozen Slice-8 composition shape's GpuFrameFlightController? threading, which is out of this commit's scope. TextureParameters.cs and BufferUsageExtensions.cs (zero callers each) are deleted outright. common.glsl is deleted: nothing in the actual Vulkan .spv build reads it. tools/ShaderCompiler/Program.cs compiles each .vert/.frag pair directly and tools/ShaderCompiler/VulkanGlslPreamble.cs injects its own complete self-contained preamble per file; common.glsl's textual concatenation was exclusively Shader.cs's GL-only mechanism, deleted at Commit 2. The five shader files that named it in comments (mesh_modern.vert, particle.vert, particle.frag, sky.frag, terrain_modern.frag) are corrected to point at VulkanGlslPreamble.cs instead. mesh.vert/mesh.frag — the pre-N.5 legacy shader pair the mandatory modern path already made unreachable, with zero C# consumers and no compiled .spv — are deleted too. Regenerated via tools/compile-shaders.ps1: 9/9 remaining shader pairs compile (previously 9/10, with mesh the sole failure — the VulkanShaderManifestTests doc comment's "nine of ten are not Vulkan-expressible" was already stale before this commit). Test fallout: dead-subject test methods/files are deleted rather than patched (TextRendererFailureSafetyTests.cs, ClipFrameUploadTests.cs, GpuResourceRetirementTransactionTests.cs's GL queue tests, one WorldRenderDiagnosticsTests source-order test, one RenderFrameResourceControllerTests clear-phase-order test); tests whose subject moved or was renamed are updated in place rather than deleted (GpuContractTests, VulkanCapabilityGateTests, MeshPipelineDeviceSeamTests' pinned seven-member surface now reads six, ParticleBindlessInstanceTests' cross-dialect check now covers the one surviving dialect, WbMeshAdapterTests' misleadingly-named null-gl test — gpuDevice was always the parameter that actually threw). Build: `dotnet build AcDream.slnx -c Release` — 0 warnings, 0 errors, with the Silk.NET.OpenGL/.Extensions.ARB package references physically removed from the csproj (not just unreferenced in code). Tests: full-solution `dotnet test` green across every project. Zero remaining `using Silk.NET.OpenGL` anywhere in src/ or tests/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
197 lines
7.7 KiB
GLSL
197 lines
7.7 KiB
GLSL
#version 460 core
|
|
#extension GL_ARB_bindless_texture : require
|
|
|
|
// Phase N.5b: terrain fragment shader on the modern bindless dispatcher.
|
|
// Math identical to terrain.frag (Phase 3c per-cell maskBlend3 +
|
|
// Phase G fog + lightning flash).
|
|
//
|
|
// Texture reads go through ACDREAM_SAMPLE_ARRAY, which
|
|
// tools/ShaderCompiler/VulkanGlslPreamble.cs injects to index the set-2
|
|
// descriptor array. GL is deleted (Campaign V slice V11); before that this
|
|
// macro expanded (via the now-deleted common.glsl) to the uvec2-handle +
|
|
// sampler2DArray-constructor pattern this shader used on that arm — the
|
|
// documented "always works" form per the ARB_bindless_texture spec, and the
|
|
// one that avoided the GL_INVALID_OPERATION the alternative (`uniform
|
|
// sampler2DArray` set via glProgramUniformHandleARB) produced on at least one
|
|
// driver in practice. The extension requirement above is dropped for Vulkan
|
|
// by the compiler's preamble, where it would be an error rather than a no-op.
|
|
|
|
in vec2 vBaseUV;
|
|
in vec3 vWorldNormal;
|
|
in vec3 vWorldPos;
|
|
in vec3 vLightingRGB;
|
|
in vec4 vOverlay0;
|
|
in vec4 vOverlay1;
|
|
in vec4 vOverlay2;
|
|
in vec4 vRoad0;
|
|
in vec4 vRoad1;
|
|
flat in float vBaseTexIdx;
|
|
|
|
out vec4 fragColor;
|
|
|
|
// Campaign V slice V2b (2026-07-27): uTerrainHandle/uAlphaHandle (uvec2, raw
|
|
// ARB_bindless_texture handles) became uTextureIndexA/uTextureIndexB (slots
|
|
// into the global texture table, ACDREAM_TEXTURE_HANDLE injected by
|
|
// tools/ShaderCompiler/VulkanGlslPreamble.cs). Named to match the pinned
|
|
// GpuPushConstants.TextureIndexA/B fields so V4d's
|
|
// move to push constants is a rename, not a redesign — there is no
|
|
// push-constant plumbing yet, so these stay plain uniforms for now.
|
|
uniform uint uTextureIndexA;
|
|
uniform uint uTextureIndexB;
|
|
|
|
// Campaign V slice V6f-3: the two atlases are sampled through the
|
|
// dialect-neutral table read instead of a GL sampler-from-handle constructor.
|
|
// `sampler2DArray(handle)` is a GL_ARB_bindless_texture form with no Vulkan
|
|
// equivalent — Vulkan's table is an opaque descriptor array in set 2, and there
|
|
// is no handle to construct a sampler from. ACDREAM_SAMPLE_ARRAY asks the
|
|
// question both dialects can answer ("sample table slot N at these
|
|
// coordinates") and expands to the right thing on each.
|
|
//
|
|
// A SAMPLING macro, not a sampler-returning one, for the reason
|
|
// VulkanGlslPreamble.cs records: under Vulkan the expansion carries `nonuniformEXT` on the indexing
|
|
// expression, and binding the result to a local sampler2DArray first is exactly
|
|
// where an implementation may drop that qualifier. The old `#define uTerrain
|
|
// sampler2DArray(...)` was that shape textually, so keeping it would have
|
|
// reintroduced the hazard at every use site.
|
|
#define sampleTerrain(uvw) ACDREAM_SAMPLE_ARRAY(uTextureIndexA, uvw)
|
|
#define sampleAlpha(uvw) ACDREAM_SAMPLE_ARRAY(uTextureIndexB, uvw)
|
|
|
|
// Campaign V slice V6f-2: the 36 per-layer tiling factors moved out of a loose
|
|
// `uniform float uTexTiling[36]` and into the uniform buffer GpuBindingModel
|
|
// reserved binding 3 for. Vulkan GLSL has no default uniform block, so the loose
|
|
// array was unspellable there, and at 144 bytes of payload it cannot ride the
|
|
// 96-byte push-constant block either. A uniform buffer is the only legal home,
|
|
// and the same declaration is legal in both dialects.
|
|
//
|
|
// The ELEMENT TYPE is deliberately unchanged. std140 pads every array element to
|
|
// 16 bytes, so the block is 576 bytes rather than 144, and packing four floats
|
|
// per vec4 would be tighter — but it would also change every use site below.
|
|
// Keeping `float uTexTiling[36]` means `uTexTiling[int(layer)]` reads exactly as
|
|
// it did before, so this commit's pixel gate is measuring the move to a uniform
|
|
// buffer and nothing else. TerrainTextureTilingTable.UniformBufferBytes and the
|
|
// stride beside it are the CPU half of this layout.
|
|
layout(std140, ACDREAM_UBO_SET binding = 3) uniform TerrainTiling {
|
|
float uTexTiling[36];
|
|
};
|
|
|
|
struct Light {
|
|
vec4 posAndKind;
|
|
vec4 dirAndRange;
|
|
vec4 colorAndIntensity;
|
|
vec4 coneAngleEtc;
|
|
};
|
|
layout(std140, ACDREAM_UBO_SET binding = 1) uniform SceneLighting {
|
|
Light uLights[8];
|
|
vec4 uCellAmbient;
|
|
vec4 uFogParams;
|
|
vec4 uFogColor;
|
|
vec4 uCameraAndTime;
|
|
};
|
|
|
|
// Retail TexMerge::CopyAndTile (0x00503580) and TexMerge::Merge
|
|
// (0x005038C0) pass TerrainTex::tex_tiling to every terrain source before
|
|
// the cell-scale alpha mask is applied. The atlas stores that value by layer.
|
|
float terrainTiling(float layer) {
|
|
return uTexTiling[int(layer)];
|
|
}
|
|
|
|
vec4 maskBlend3(vec4 t0, vec4 t1, vec4 t2, float h0, float h1, float h2) {
|
|
float a0 = h0 == 0.0 ? 1.0 : t0.a;
|
|
float a1 = h1 == 0.0 ? 1.0 : t1.a;
|
|
float a2 = h2 == 0.0 ? 1.0 : t2.a;
|
|
float aR = 1.0 - (a0 * a1 * a2);
|
|
float aRsafe = max(aR, 1e-6);
|
|
a0 = 1.0 - a0;
|
|
a1 = 1.0 - a1;
|
|
a2 = 1.0 - a2;
|
|
vec3 r0 = (a0 * t0.rgb + (1.0 - a0) * a1 * t1.rgb + (1.0 - a1) * a2 * t2.rgb);
|
|
return vec4(r0 / aRsafe, aR);
|
|
}
|
|
|
|
vec4 combineOverlays(vec2 baseUV, vec4 pOverlay0, vec4 pOverlay1, vec4 pOverlay2) {
|
|
float h0 = pOverlay0.z < 0.0 ? 0.0 : 1.0;
|
|
float h1 = pOverlay1.z < 0.0 ? 0.0 : 1.0;
|
|
float h2 = pOverlay2.z < 0.0 ? 0.0 : 1.0;
|
|
vec4 t0 = vec4(0.0), t1 = vec4(0.0), t2 = vec4(0.0);
|
|
|
|
if (h0 > 0.0) {
|
|
t0 = sampleTerrain(vec3(baseUV * terrainTiling(pOverlay0.z), pOverlay0.z));
|
|
if (pOverlay0.w >= 0.0) {
|
|
vec4 a = sampleAlpha(vec3(pOverlay0.xy, pOverlay0.w));
|
|
t0.a = a.a;
|
|
}
|
|
}
|
|
if (h1 > 0.0) {
|
|
t1 = sampleTerrain(vec3(baseUV * terrainTiling(pOverlay1.z), pOverlay1.z));
|
|
if (pOverlay1.w >= 0.0) {
|
|
vec4 a = sampleAlpha(vec3(pOverlay1.xy, pOverlay1.w));
|
|
t1.a = a.a;
|
|
}
|
|
}
|
|
if (h2 > 0.0) {
|
|
t2 = sampleTerrain(vec3(baseUV * terrainTiling(pOverlay2.z), pOverlay2.z));
|
|
if (pOverlay2.w >= 0.0) {
|
|
vec4 a = sampleAlpha(vec3(pOverlay2.xy, pOverlay2.w));
|
|
t2.a = a.a;
|
|
}
|
|
}
|
|
return maskBlend3(t0, t1, t2, h0, h1, h2);
|
|
}
|
|
|
|
vec4 combineRoad(vec2 baseUV, vec4 pRoad0, vec4 pRoad1) {
|
|
float h0 = pRoad0.z < 0.0 ? 0.0 : 1.0;
|
|
float h1 = pRoad1.z < 0.0 ? 0.0 : 1.0;
|
|
vec4 result = vec4(0.0);
|
|
if (h0 > 0.0) {
|
|
result = sampleTerrain(vec3(baseUV * terrainTiling(pRoad0.z), pRoad0.z));
|
|
if (pRoad0.w >= 0.0) {
|
|
vec4 a0 = sampleAlpha(vec3(pRoad0.xy, pRoad0.w));
|
|
result.a = 1.0 - a0.a;
|
|
if (h1 > 0.0 && pRoad1.w >= 0.0) {
|
|
vec4 a1 = sampleAlpha(vec3(pRoad1.xy, pRoad1.w));
|
|
result.a = 1.0 - (a0.a * a1.a);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
vec3 applyFog(vec3 lit, vec3 worldPos) {
|
|
int mode = int(uFogParams.w);
|
|
if (mode == 0) return lit;
|
|
float d = length(worldPos - uCameraAndTime.xyz);
|
|
float fogStart = uFogParams.x;
|
|
float fogEnd = uFogParams.y;
|
|
float span = max(1e-3, fogEnd - fogStart);
|
|
float fog = clamp((d - fogStart) / span, 0.0, 1.0);
|
|
return mix(lit, uFogColor.xyz, fog);
|
|
}
|
|
|
|
void main() {
|
|
vec4 baseColor = vec4(0.0);
|
|
if (vBaseTexIdx >= 0.0) {
|
|
baseColor = sampleTerrain(vec3(vBaseUV * terrainTiling(vBaseTexIdx), vBaseTexIdx));
|
|
}
|
|
|
|
vec4 overlays = vec4(0.0);
|
|
if (vOverlay0.z >= 0.0)
|
|
overlays = combineOverlays(vBaseUV, vOverlay0, vOverlay1, vOverlay2);
|
|
|
|
vec4 roads = vec4(0.0);
|
|
if (vRoad0.z >= 0.0)
|
|
roads = combineRoad(vBaseUV, vRoad0, vRoad1);
|
|
|
|
vec3 baseMasked = baseColor.rgb * ((1.0 - overlays.a) * (1.0 - roads.a));
|
|
vec3 ovlMasked = overlays.rgb * (overlays.a * (1.0 - roads.a));
|
|
vec3 roadMasked = roads.rgb * roads.a;
|
|
vec3 rgb = clamp(baseMasked + ovlMasked + roadMasked, 0.0, 1.0);
|
|
|
|
vec3 lit = rgb * min(vLightingRGB, vec3(1.0));
|
|
|
|
float flash = uFogParams.z;
|
|
lit += flash * vec3(0.6, 0.6, 0.75);
|
|
|
|
lit = applyFog(lit, vWorldPos);
|
|
|
|
fragColor = vec4(lit, 1.0);
|
|
}
|