From eeee8acc6c65ac9f499843e7d067f14a82960e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 05:17:20 +0300 Subject: [PATCH 01/17] Rename ShaderFlags to ShaderRenderStateFlags --- Source.Common/MaterialSystem/IShaderSystem.cs | 2 +- Source.Common/ShaderAPI/IShaderShadow.cs | 6 +++--- Source.MaterialSystem/Material.cs | 2 +- Source.ShaderAPI.Gl46/ShaderShadow.cs | 6 +++--- Source.ShaderAPI.Gl46/ShaderSystem.cs | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Source.Common/MaterialSystem/IShaderSystem.cs b/Source.Common/MaterialSystem/IShaderSystem.cs index f77ca68e9..65bb0eed8 100644 --- a/Source.Common/MaterialSystem/IShaderSystem.cs +++ b/Source.Common/MaterialSystem/IShaderSystem.cs @@ -16,7 +16,7 @@ public enum ShaderUsingFlags public interface IShaderSystem { public static bool IsTranslucent(IShaderShadow renderState) { - return (renderState.GetFlags() & ShaderFlags.OpacityTranslucent) != 0; + return (renderState.GetFlags() & ShaderRenderStateFlags.OpacityTranslucent) != 0; } void BindTexture(Sampler sampler, ITexture texture, int frame = 0); diff --git a/Source.Common/ShaderAPI/IShaderShadow.cs b/Source.Common/ShaderAPI/IShaderShadow.cs index f90ccb8ab..217d3857e 100644 --- a/Source.Common/ShaderAPI/IShaderShadow.cs +++ b/Source.Common/ShaderAPI/IShaderShadow.cs @@ -227,7 +227,7 @@ public enum StandardTextureId Max = 32 } -public enum ShaderFlags +public enum ShaderRenderStateFlags { OpacityAlphaTest = 0x0010, OpacityOpaque = 0x0020, @@ -328,8 +328,8 @@ public interface IShaderShadow VertexFormat GetVertexFormat(); void Activate(); - ShaderFlags GetFlags(); - void SetFlags(ShaderFlags flags); + ShaderRenderStateFlags GetFlags(); + void SetFlags(ShaderRenderStateFlags flags); void EnableSRGBRead(Sampler sampler, bool state); void EnableSRGBWrite(bool state); } diff --git a/Source.MaterialSystem/Material.cs b/Source.MaterialSystem/Material.cs index 2a3407b9c..0b2a3e67b 100644 --- a/Source.MaterialSystem/Material.cs +++ b/Source.MaterialSystem/Material.cs @@ -1361,7 +1361,7 @@ public bool IsTwoSided() { public bool IsAlphaTested() { Precache(); if (Shader != null && IsValidRenderState()) - return (ShaderRenderState.GetFlags() & ShaderFlags.OpacityAlphaTest) != 0 || GetMaterialVarFlag(MaterialVarFlags.AlphaTest); + return (ShaderRenderState.GetFlags() & ShaderRenderStateFlags.OpacityAlphaTest) != 0 || GetMaterialVarFlag(MaterialVarFlags.AlphaTest); return false; } diff --git a/Source.ShaderAPI.Gl46/ShaderShadow.cs b/Source.ShaderAPI.Gl46/ShaderShadow.cs index f86a536e5..544d6748a 100644 --- a/Source.ShaderAPI.Gl46/ShaderShadow.cs +++ b/Source.ShaderAPI.Gl46/ShaderShadow.cs @@ -123,9 +123,9 @@ public unsafe void Dispose() { glDeleteBuffers(BASE_UBO, VERTEX_UBO, PIXEL_UBO); } - ShaderFlags Flags; - public ShaderFlags GetFlags() => Flags; - public void SetFlags(ShaderFlags flags) => Flags = flags; + ShaderRenderStateFlags Flags; + public ShaderRenderStateFlags GetFlags() => Flags; + public void SetFlags(ShaderRenderStateFlags flags) => Flags = flags; public unsafe void Activate() { CreateShaderObjects(); // Recreate UBO's, if we were lazy-loaded diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index ff5a61004..b228c9b93 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -293,12 +293,12 @@ public bool InitRenderState(IShader shader, IMaterialVar[] shaderParams, ref ISh private void ComputeRenderStateFlagsFromSnapshot(IShaderShadow renderState) { if (ShaderAPI.IsTranslucent(renderState)) - renderState.SetFlags(renderState.GetFlags() | ShaderFlags.OpacityTranslucent); + renderState.SetFlags(renderState.GetFlags() | ShaderRenderStateFlags.OpacityTranslucent); else { if (ShaderAPI.IsAlphaTested(renderState)) - renderState.SetFlags(renderState.GetFlags() | ShaderFlags.OpacityAlphaTest); + renderState.SetFlags(renderState.GetFlags() | ShaderRenderStateFlags.OpacityAlphaTest); else - renderState.SetFlags(renderState.GetFlags() | ShaderFlags.OpacityOpaque); + renderState.SetFlags(renderState.GetFlags() | ShaderRenderStateFlags.OpacityOpaque); } } From a31814bb0f9aa0fd844e9947a45477647f54875a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 05:19:25 +0300 Subject: [PATCH 02/17] Add ShaderFlags enum and return it from IShader.GetFlags --- Source.Common/MaterialSystem/IShader.cs | 8 +++++++- Source.StdShader.Gl46/BaseShader.cs | 2 +- Source.StdShader.Gl46/BlurFilterX.cs | 4 ++-- Source.StdShader.Gl46/BlurFilterY.cs | 4 ++-- Source.StdShader.Gl46/Downsample_nohdr.cs | 4 ++-- Source.StdShader.Gl46/EyeGlint.cs | 2 +- Source.StdShader.Gl46/Eyes.cs | 4 ++-- Source.StdShader.Gl46/IntroScreenSpaceEffect.cs | 4 ++-- Source.StdShader.Gl46/LightmappedGeneric.cs | 4 ++-- Source.StdShader.Gl46/Occlusion.cs | 4 ++-- Source.StdShader.Gl46/Screenspace_General.cs | 2 +- Source.StdShader.Gl46/Shadow.cs | 4 ++-- Source.StdShader.Gl46/ShadowModel.cs | 4 ++-- Source.StdShader.Gl46/Sky.cs | 2 +- Source.StdShader.Gl46/Sky_DX9.cs | 2 +- Source.StdShader.Gl46/Sky_HDR_DX9.cs | 4 ++-- Source.StdShader.Gl46/Teeth.cs | 4 ++-- Source.StdShader.Gl46/UnlitGeneric.cs | 4 ++-- Source.StdShader.Gl46/UnlitTwoTexture.cs | 4 ++-- Source.StdShader.Gl46/VertexLitGeneric.cs | 4 ++-- Source.StdShader.Gl46/Water.cs | 2 +- Source.StdShader.Gl46/Water_DX90.cs | 4 ++-- Source.StdShader.Gl46/Wireframe.cs | 2 +- Source.StdShader.Gl46/WorldVertexTransition.cs | 4 ++-- Source.StdShader.Gl46/g_Sky.cs | 4 ++-- Source.StdShader.Gl46/screenspace_general_dx9.cs | 4 ++-- 26 files changed, 50 insertions(+), 44 deletions(-) diff --git a/Source.Common/MaterialSystem/IShader.cs b/Source.Common/MaterialSystem/IShader.cs index f9890eed3..667ecff5c 100644 --- a/Source.Common/MaterialSystem/IShader.cs +++ b/Source.Common/MaterialSystem/IShader.cs @@ -16,11 +16,17 @@ public struct ShaderParamInfo public ShaderParamFlags Flags; } +[Flags] +public enum ShaderFlags +{ + None = 0, + NotEditable = 0x1 +} public interface IShader { string? GetName(); - int GetFlags(); + ShaderFlags GetFlags(); int GetNumParams(); ReadOnlySpan GetParamName(int paramIndex); ReadOnlySpan GetParamHelp(int paramIndex); diff --git a/Source.StdShader.Gl46/BaseShader.cs b/Source.StdShader.Gl46/BaseShader.cs index 5e859178e..8960c0759 100644 --- a/Source.StdShader.Gl46/BaseShader.cs +++ b/Source.StdShader.Gl46/BaseShader.cs @@ -38,7 +38,7 @@ public abstract class BaseShader : IShader ]; public string GetName() => GetType().Name; - public virtual int GetFlags() => 0; + public virtual ShaderFlags GetFlags() => ShaderFlags.None; public int CurrentMaterialVarFlags() { return Params![(int)ShaderMaterialVars.Flags].GetIntValue(); diff --git a/Source.StdShader.Gl46/BlurFilterX.cs b/Source.StdShader.Gl46/BlurFilterX.cs index dca61cc78..3deb37ac2 100644 --- a/Source.StdShader.Gl46/BlurFilterX.cs +++ b/Source.StdShader.Gl46/BlurFilterX.cs @@ -7,10 +7,10 @@ namespace Source.StdShader.Gl46; public class BlurFilterX : BaseVSShader { public static string HelpString = "Help for BlurFilterX"; - public static int Flags = (int)ShaderParamFlags.NotEditable; + public static ShaderFlags Flags = ShaderFlags.NotEditable; public override string? GetFallbackShader(IMaterialVar[] vars) => null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; protected override void OnInitShaderInstance(IMaterialVar[] vars, ReadOnlySpan materialName) { if (vars[(int)ShaderMaterialVars.BaseTexture].IsDefined()) diff --git a/Source.StdShader.Gl46/BlurFilterY.cs b/Source.StdShader.Gl46/BlurFilterY.cs index f17a9cf98..c0b840477 100644 --- a/Source.StdShader.Gl46/BlurFilterY.cs +++ b/Source.StdShader.Gl46/BlurFilterY.cs @@ -7,7 +7,7 @@ namespace Source.StdShader.Gl46; public class BlurFilterY : BaseVSShader { public static string HelpString = "Help for BlurFilterY"; - public static int Flags = (int)ShaderParamFlags.NotEditable; + public static ShaderFlags Flags = ShaderFlags.NotEditable; public static List ShaderParams = []; public class ShaderParam @@ -35,7 +35,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public static readonly ShaderParam FRAMETEXTURE = new($"${nameof(FRAMETEXTURE)}", ShaderParamType.Texture, "_rt_SmallHDR0", ""); public override string? GetFallbackShader(IMaterialVar[] vars) => null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/Downsample_nohdr.cs b/Source.StdShader.Gl46/Downsample_nohdr.cs index 897663264..69e5bd542 100644 --- a/Source.StdShader.Gl46/Downsample_nohdr.cs +++ b/Source.StdShader.Gl46/Downsample_nohdr.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; public class Downsample_nohdr : BaseVSShader { public static string HelpString = "Help for Downsample_nohdr"; - public static int Flags = (int)ShaderParamFlags.NotEditable; + public static ShaderFlags Flags = ShaderFlags.NotEditable; public static List ShaderParams = []; public class ShaderParam @@ -41,7 +41,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default static readonly ConVar r_bloomtintexponent = new("r_bloomtintexponent", "2.2"); public override string? GetFallbackShader(IMaterialVar[] vars) => null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/EyeGlint.cs b/Source.StdShader.Gl46/EyeGlint.cs index c559dd7ce..b3b42a251 100644 --- a/Source.StdShader.Gl46/EyeGlint.cs +++ b/Source.StdShader.Gl46/EyeGlint.cs @@ -7,7 +7,7 @@ namespace Source.StdShader.Gl46; public class EyeGlint : BaseVSShader { public static string HelpString = "Help for EyeGlint"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public override string? GetFallbackShader(IMaterialVar[] vars) => null; diff --git a/Source.StdShader.Gl46/Eyes.cs b/Source.StdShader.Gl46/Eyes.cs index 3fd723177..8ceccbf80 100644 --- a/Source.StdShader.Gl46/Eyes.cs +++ b/Source.StdShader.Gl46/Eyes.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; public class Eyes : BaseVSShader { public static string HelpString = "Help for Eyes"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -89,7 +89,7 @@ protected override void OnInitShaderParams(IMaterialVar[] parms, ReadOnlySpan null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs b/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs index 76d0109aa..f113aaf43 100644 --- a/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs +++ b/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; public class IntroScreenSpaceEffect : BaseVSShader { public static string HelpString = "Help for IntroScreenSpaceEffect"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -61,7 +61,7 @@ protected override void OnInitShaderParams(IMaterialVar[] parms, ReadOnlySpan null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/LightmappedGeneric.cs b/Source.StdShader.Gl46/LightmappedGeneric.cs index 79ac64fd1..6ba09097e 100644 --- a/Source.StdShader.Gl46/LightmappedGeneric.cs +++ b/Source.StdShader.Gl46/LightmappedGeneric.cs @@ -10,7 +10,7 @@ public class LightmappedGeneric : BaseVSShader { public static string HelpString = "Help for LightmappedGeneric"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -95,7 +95,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public static readonly ShaderParam OUTLINEEND1 = new($"${nameof(OUTLINEEND1)}", ShaderParamType.Float, "0.0", "outer end value for outline"); public override string? GetFallbackShader(IMaterialVar[] vars) => null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/Occlusion.cs b/Source.StdShader.Gl46/Occlusion.cs index 5951ed3fa..05bf2ebfc 100644 --- a/Source.StdShader.Gl46/Occlusion.cs +++ b/Source.StdShader.Gl46/Occlusion.cs @@ -7,7 +7,7 @@ public class Occlusion : BaseVSShader { public static string HelpString = "Help for Occlusion"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -48,7 +48,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public int GetFlags() => (int)Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/Screenspace_General.cs b/Source.StdShader.Gl46/Screenspace_General.cs index 794a440e3..d89e85e01 100644 --- a/Source.StdShader.Gl46/Screenspace_General.cs +++ b/Source.StdShader.Gl46/Screenspace_General.cs @@ -5,7 +5,7 @@ namespace Source.StdShader.Gl46; public class Screenspace_General : BaseVSShader { public static string HelpString = "Help for screenspace_general"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public override string? GetFallbackShader(IMaterialVar[] vars) => "screenspace_general_dx9"; } diff --git a/Source.StdShader.Gl46/Shadow.cs b/Source.StdShader.Gl46/Shadow.cs index b3505c091..422e91f9c 100644 --- a/Source.StdShader.Gl46/Shadow.cs +++ b/Source.StdShader.Gl46/Shadow.cs @@ -9,7 +9,7 @@ public class Shadow : BaseVSShader { public static string HelpString = "Help for Shadow"; - public static int Flags = (int)ShaderParamFlags.NotEditable; + public static ShaderFlags Flags = ShaderFlags.NotEditable; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -58,7 +58,7 @@ protected override void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/ShadowModel.cs b/Source.StdShader.Gl46/ShadowModel.cs index c20689a99..0862da49c 100644 --- a/Source.StdShader.Gl46/ShadowModel.cs +++ b/Source.StdShader.Gl46/ShadowModel.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; public class ShadowModel : BaseVSShader { public static string HelpString = "Help for ShadowModel"; - public static int Flags = (int)ShaderParamFlags.NotEditable; + public static ShaderFlags Flags = ShaderFlags.NotEditable; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -71,7 +71,7 @@ protected override void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/Sky.cs b/Source.StdShader.Gl46/Sky.cs index dfdca8589..100b7d330 100644 --- a/Source.StdShader.Gl46/Sky.cs +++ b/Source.StdShader.Gl46/Sky.cs @@ -5,7 +5,7 @@ namespace Source.StdShader.Gl46; public class Sky : BaseVSShader { public static string HelpString = "Help for Sky"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public override string? GetFallbackShader(IMaterialVar[] vars) => "Sky_HDR_DX9"; } diff --git a/Source.StdShader.Gl46/Sky_DX9.cs b/Source.StdShader.Gl46/Sky_DX9.cs index ae7831124..f35166907 100644 --- a/Source.StdShader.Gl46/Sky_DX9.cs +++ b/Source.StdShader.Gl46/Sky_DX9.cs @@ -9,7 +9,7 @@ namespace Source.StdShader.Gl46; public class Sky_DX9 : BaseVSShader { public static string HelpString = "Help for Sky_DX9 shader"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public override string? GetFallbackShader(IMaterialVar[] vars) => null; diff --git a/Source.StdShader.Gl46/Sky_HDR_DX9.cs b/Source.StdShader.Gl46/Sky_HDR_DX9.cs index 402245b17..07fcb8e6f 100644 --- a/Source.StdShader.Gl46/Sky_HDR_DX9.cs +++ b/Source.StdShader.Gl46/Sky_HDR_DX9.cs @@ -10,7 +10,7 @@ namespace Source.StdShader.Gl46; public class Sky_HDR_DX9 : BaseVSShader { public static string HelpString = "Help for Sky_HDR_DX9 shader"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public class ShaderParam @@ -47,7 +47,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default return "Sky_DX9"; return null; } - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/Teeth.cs b/Source.StdShader.Gl46/Teeth.cs index 7937c6d14..cd58d5ac0 100644 --- a/Source.StdShader.Gl46/Teeth.cs +++ b/Source.StdShader.Gl46/Teeth.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; public class Teeth : BaseVSShader { public static string HelpString = "Help for Teeth_DX9"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -71,7 +71,7 @@ protected override void OnInitShaderParams(IMaterialVar[] parms, ReadOnlySpan null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/UnlitGeneric.cs b/Source.StdShader.Gl46/UnlitGeneric.cs index b61a4fe4f..6e8c545c1 100644 --- a/Source.StdShader.Gl46/UnlitGeneric.cs +++ b/Source.StdShader.Gl46/UnlitGeneric.cs @@ -7,7 +7,7 @@ public class UnlitGeneric : BaseVSShader { public static string HelpString = "Help for UnlitGeneric"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -76,7 +76,7 @@ protected override void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/UnlitTwoTexture.cs b/Source.StdShader.Gl46/UnlitTwoTexture.cs index 41d6dbafe..37443a4fb 100644 --- a/Source.StdShader.Gl46/UnlitTwoTexture.cs +++ b/Source.StdShader.Gl46/UnlitTwoTexture.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; public class UnlitTwoTexture : BaseVSShader { public static string HelpString = "Help for UnlitTwoTexture"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -60,7 +60,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public static readonly ShaderParam REFRACTAMOUNT = new($"${nameof(REFRACTAMOUNT)}", ShaderParamType.Float, "2", ""); public override string? GetFallbackShader(IMaterialVar[] vars) => null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/VertexLitGeneric.cs b/Source.StdShader.Gl46/VertexLitGeneric.cs index a4479e3e7..7726e8253 100644 --- a/Source.StdShader.Gl46/VertexLitGeneric.cs +++ b/Source.StdShader.Gl46/VertexLitGeneric.cs @@ -10,7 +10,7 @@ public class VertexLitGeneric : BaseVSShader { public static string HelpString = "Help for VertexLitGeneric"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -185,7 +185,7 @@ protected override void OnInitShaderParams(IMaterialVar[] parms, ReadOnlySpan null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/Water.cs b/Source.StdShader.Gl46/Water.cs index 3cb8ba327..f30797c38 100644 --- a/Source.StdShader.Gl46/Water.cs +++ b/Source.StdShader.Gl46/Water.cs @@ -5,7 +5,7 @@ namespace Source.StdShader.Gl46; public class Water : BaseVSShader { public static string HelpString = "Help for Water"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public override string? GetFallbackShader(IMaterialVar[] vars) => "Water_DX9_HDR"; } diff --git a/Source.StdShader.Gl46/Water_DX90.cs b/Source.StdShader.Gl46/Water_DX90.cs index 2385703c1..cb971a451 100644 --- a/Source.StdShader.Gl46/Water_DX90.cs +++ b/Source.StdShader.Gl46/Water_DX90.cs @@ -10,7 +10,7 @@ namespace Source.StdShader.Gl46; public class Water_DX90 : BaseVSShader { public static string HelpString = "Help for Water"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public class ShaderParam @@ -69,7 +69,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default const float PSHADER_VECT_SCALE = 1.0f; const float VSHADER_VECT_SCALE = 1.0f; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/Wireframe.cs b/Source.StdShader.Gl46/Wireframe.cs index ee53bb450..972728df5 100644 --- a/Source.StdShader.Gl46/Wireframe.cs +++ b/Source.StdShader.Gl46/Wireframe.cs @@ -6,7 +6,7 @@ namespace Source.StdShader.Gl46; public class Wireframe : BaseVSShader { public static string HelpString = "Help for Wireframe"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; protected override void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan materialName) { vars[(int)ShaderMaterialVars.BaseTexture].SetStringValue("white"); InitParamsUnlitGeneric((int)ShaderMaterialVars.BaseTexture, -1, -1, -1, -1, -1, -1); diff --git a/Source.StdShader.Gl46/WorldVertexTransition.cs b/Source.StdShader.Gl46/WorldVertexTransition.cs index ff096d24a..15ae045ba 100644 --- a/Source.StdShader.Gl46/WorldVertexTransition.cs +++ b/Source.StdShader.Gl46/WorldVertexTransition.cs @@ -9,7 +9,7 @@ public class WorldVertexTransition : BaseVSShader { public static string HelpString = "Help for WorldVertexTransition"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -151,7 +151,7 @@ protected override void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/g_Sky.cs b/Source.StdShader.Gl46/g_Sky.cs index 9a389fe8f..0c1f9e43a 100644 --- a/Source.StdShader.Gl46/g_Sky.cs +++ b/Source.StdShader.Gl46/g_Sky.cs @@ -8,7 +8,7 @@ public class g_Sky : BaseVSShader { public static string HelpString = "Help for g_Sky"; - public static int Flags = 0; + public static ShaderFlags Flags = ShaderFlags.None; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -77,7 +77,7 @@ protected override void OnInitShaderParams(IMaterialVar[] vars, ReadOnlySpan Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); diff --git a/Source.StdShader.Gl46/screenspace_general_dx9.cs b/Source.StdShader.Gl46/screenspace_general_dx9.cs index fa0905603..a6f1677cb 100644 --- a/Source.StdShader.Gl46/screenspace_general_dx9.cs +++ b/Source.StdShader.Gl46/screenspace_general_dx9.cs @@ -8,7 +8,7 @@ namespace Source.StdShader.Gl46; public class screenspace_general_dx9 : BaseVSShader { public static string HelpString = "Help for screenspace_general"; - public static int Flags = (int)ShaderParamFlags.NotEditable; + public static ShaderFlags Flags = ShaderFlags.NotEditable; public static List ShaderParams = []; public static ShaderParam[] ShaderParamOverrides = new ShaderParam[(int)ShaderMaterialVars.Count]; @@ -113,7 +113,7 @@ protected override void OnInitShaderParams(IMaterialVar[] parms, ReadOnlySpan null; - public override int GetFlags() => Flags; + public override ShaderFlags GetFlags() => Flags; public override int GetNumParams() => base.GetNumParams() + ShaderParams.Count; public override ReadOnlySpan GetParamName(int paramIndex) { int baseClassParamCount = base.GetNumParams(); From f23a626ba05d873f72c4005e5ac763369474798d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 05:21:44 +0300 Subject: [PATCH 03/17] Use ShaderParamFlags instead of int in ShaderParam --- Source.StdShader.Gl46/BlurFilterY.cs | 6 +++--- Source.StdShader.Gl46/Downsample_nohdr.cs | 6 +++--- Source.StdShader.Gl46/Eyes.cs | 10 +++++----- Source.StdShader.Gl46/IntroScreenSpaceEffect.cs | 10 +++++----- Source.StdShader.Gl46/LightmappedGeneric.cs | 10 +++++----- Source.StdShader.Gl46/Occlusion.cs | 10 +++++----- Source.StdShader.Gl46/Shadow.cs | 10 +++++----- Source.StdShader.Gl46/ShadowModel.cs | 10 +++++----- Source.StdShader.Gl46/Sky_HDR_DX9.cs | 6 +++--- Source.StdShader.Gl46/Teeth.cs | 10 +++++----- Source.StdShader.Gl46/UnlitGeneric.cs | 10 +++++----- Source.StdShader.Gl46/UnlitTwoTexture.cs | 10 +++++----- Source.StdShader.Gl46/VertexLitGeneric.cs | 10 +++++----- Source.StdShader.Gl46/Water_DX90.cs | 6 +++--- Source.StdShader.Gl46/WorldVertexTransition.cs | 10 +++++----- Source.StdShader.Gl46/g_Sky.cs | 10 +++++----- Source.StdShader.Gl46/screenspace_general_dx9.cs | 10 +++++----- 17 files changed, 77 insertions(+), 77 deletions(-) diff --git a/Source.StdShader.Gl46/BlurFilterY.cs b/Source.StdShader.Gl46/BlurFilterY.cs index c0b840477..df8243305 100644 --- a/Source.StdShader.Gl46/BlurFilterY.cs +++ b/Source.StdShader.Gl46/BlurFilterY.cs @@ -14,12 +14,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -27,7 +27,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/Downsample_nohdr.cs b/Source.StdShader.Gl46/Downsample_nohdr.cs index 69e5bd542..23f617419 100644 --- a/Source.StdShader.Gl46/Downsample_nohdr.cs +++ b/Source.StdShader.Gl46/Downsample_nohdr.cs @@ -15,12 +15,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -28,7 +28,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/Eyes.cs b/Source.StdShader.Gl46/Eyes.cs index 8ceccbf80..bc9a1719a 100644 --- a/Source.StdShader.Gl46/Eyes.cs +++ b/Source.StdShader.Gl46/Eyes.cs @@ -16,12 +16,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -33,12 +33,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -46,7 +46,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs b/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs index f113aaf43..9222cdfdb 100644 --- a/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs +++ b/Source.StdShader.Gl46/IntroScreenSpaceEffect.cs @@ -16,12 +16,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -33,12 +33,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -46,7 +46,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/LightmappedGeneric.cs b/Source.StdShader.Gl46/LightmappedGeneric.cs index 6ba09097e..1dd413c4a 100644 --- a/Source.StdShader.Gl46/LightmappedGeneric.cs +++ b/Source.StdShader.Gl46/LightmappedGeneric.cs @@ -18,22 +18,22 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; AssertMsg(ShaderParamOverrides[(int)var] == null, "Shader parameter override duplicately defined!"); ShaderParamOverrides[(int)var] = this; Index = (int)var; } - public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -41,7 +41,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/Occlusion.cs b/Source.StdShader.Gl46/Occlusion.cs index 05bf2ebfc..dbedaf504 100644 --- a/Source.StdShader.Gl46/Occlusion.cs +++ b/Source.StdShader.Gl46/Occlusion.cs @@ -15,12 +15,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -32,12 +32,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -45,7 +45,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } public override ShaderFlags GetFlags() => Flags; diff --git a/Source.StdShader.Gl46/Shadow.cs b/Source.StdShader.Gl46/Shadow.cs index 422e91f9c..9840c9a7c 100644 --- a/Source.StdShader.Gl46/Shadow.cs +++ b/Source.StdShader.Gl46/Shadow.cs @@ -17,12 +17,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -34,12 +34,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -47,7 +47,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/ShadowModel.cs b/Source.StdShader.Gl46/ShadowModel.cs index 0862da49c..731558545 100644 --- a/Source.StdShader.Gl46/ShadowModel.cs +++ b/Source.StdShader.Gl46/ShadowModel.cs @@ -16,12 +16,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -33,12 +33,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -46,7 +46,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/Sky_HDR_DX9.cs b/Source.StdShader.Gl46/Sky_HDR_DX9.cs index 07fcb8e6f..243708123 100644 --- a/Source.StdShader.Gl46/Sky_HDR_DX9.cs +++ b/Source.StdShader.Gl46/Sky_HDR_DX9.cs @@ -17,12 +17,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -30,7 +30,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/Teeth.cs b/Source.StdShader.Gl46/Teeth.cs index cd58d5ac0..22c1f2204 100644 --- a/Source.StdShader.Gl46/Teeth.cs +++ b/Source.StdShader.Gl46/Teeth.cs @@ -16,12 +16,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -33,12 +33,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -46,7 +46,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/UnlitGeneric.cs b/Source.StdShader.Gl46/UnlitGeneric.cs index 6e8c545c1..079491879 100644 --- a/Source.StdShader.Gl46/UnlitGeneric.cs +++ b/Source.StdShader.Gl46/UnlitGeneric.cs @@ -15,12 +15,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -32,12 +32,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -45,7 +45,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/UnlitTwoTexture.cs b/Source.StdShader.Gl46/UnlitTwoTexture.cs index 37443a4fb..529510f8d 100644 --- a/Source.StdShader.Gl46/UnlitTwoTexture.cs +++ b/Source.StdShader.Gl46/UnlitTwoTexture.cs @@ -16,12 +16,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -33,12 +33,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -46,7 +46,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/VertexLitGeneric.cs b/Source.StdShader.Gl46/VertexLitGeneric.cs index 7726e8253..d3f1a7b97 100644 --- a/Source.StdShader.Gl46/VertexLitGeneric.cs +++ b/Source.StdShader.Gl46/VertexLitGeneric.cs @@ -18,22 +18,22 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; AssertMsg(ShaderParamOverrides[(int)var] == null, "Shader parameter override duplicately defined!"); ShaderParamOverrides[(int)var] = this; Index = (int)var; } - public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -41,7 +41,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/Water_DX90.cs b/Source.StdShader.Gl46/Water_DX90.cs index cb971a451..7b34e7a28 100644 --- a/Source.StdShader.Gl46/Water_DX90.cs +++ b/Source.StdShader.Gl46/Water_DX90.cs @@ -17,12 +17,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -30,7 +30,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/WorldVertexTransition.cs b/Source.StdShader.Gl46/WorldVertexTransition.cs index 15ae045ba..91ecd877a 100644 --- a/Source.StdShader.Gl46/WorldVertexTransition.cs +++ b/Source.StdShader.Gl46/WorldVertexTransition.cs @@ -17,12 +17,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -34,12 +34,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -47,7 +47,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/g_Sky.cs b/Source.StdShader.Gl46/g_Sky.cs index 0c1f9e43a..a31016645 100644 --- a/Source.StdShader.Gl46/g_Sky.cs +++ b/Source.StdShader.Gl46/g_Sky.cs @@ -16,12 +16,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -33,12 +33,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -46,7 +46,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } diff --git a/Source.StdShader.Gl46/screenspace_general_dx9.cs b/Source.StdShader.Gl46/screenspace_general_dx9.cs index a6f1677cb..50022d3b5 100644 --- a/Source.StdShader.Gl46/screenspace_general_dx9.cs +++ b/Source.StdShader.Gl46/screenspace_general_dx9.cs @@ -16,12 +16,12 @@ public class ShaderParam { public readonly ShaderParamInfo Info; public readonly int Index; - public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags) { + public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags) { Info.Name = "override"; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; if (ShaderParamOverrides[(int)var] == null) { @@ -33,12 +33,12 @@ public ShaderParam(ShaderMaterialVars var, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, int flags = 0) { + public ShaderParam(string name, ShaderParamType type, ReadOnlySpan defaultParam, ReadOnlySpan help, ShaderParamFlags flags = 0) { Info.Name = name; Info.Type = type; Info.DefaultValue = new(defaultParam); Info.Help = new(help); - Info.Flags = (ShaderParamFlags)flags; + Info.Flags = flags; Index = (int)ShaderMaterialVars.Count + ShaderParams.Count; ShaderParams.Add(this); } @@ -46,7 +46,7 @@ public ShaderParam(string name, ShaderParamType type, ReadOnlySpan default public ReadOnlySpan GetName() => Info.Name; public ShaderParamType GetType() => Info.Type; public ReadOnlySpan GetDefaultValue() => Info.DefaultValue; - public int GetFlags() => (int)Info.Flags; + public ShaderParamFlags GetFlags() => Info.Flags; public ReadOnlySpan GetHelp() => Info.Help; } From 1caab7f3d4f837ebbbd4af0bea7043eb55421dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 13:07:42 +0300 Subject: [PATCH 04/17] Add CreateSharedContext to IGraphicsContext --- Source.Common/Launcher/IGraphicsContext.cs | 1 + Source.SDLManager/SDL3_LauncherManager.cs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Source.Common/Launcher/IGraphicsContext.cs b/Source.Common/Launcher/IGraphicsContext.cs index 03c7a15e3..3d40737f4 100644 --- a/Source.Common/Launcher/IGraphicsContext.cs +++ b/Source.Common/Launcher/IGraphicsContext.cs @@ -6,4 +6,5 @@ public interface IGraphicsContext { void MakeCurrent(); void SetSwapInterval(float swapInterval); void SwapBuffers(); + IGraphicsContext? CreateSharedContext(); } diff --git a/Source.SDLManager/SDL3_LauncherManager.cs b/Source.SDLManager/SDL3_LauncherManager.cs index b50fe2c13..61f316a50 100644 --- a/Source.SDLManager/SDL3_LauncherManager.cs +++ b/Source.SDLManager/SDL3_LauncherManager.cs @@ -85,6 +85,21 @@ public void SetSwapInterval(float swapInterval) { public void SwapBuffers() { SDL3.SDL_GL_SwapWindow((SDL_Window*)window); } + + public IGraphicsContext? CreateSharedContext() { + MakeCurrent(); + SDL3.SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + nint shared = (nint)SDL3.SDL_GL_CreateContext((SDL_Window*)window); + SDL3.SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0); + MakeCurrent(); + + if (shared == 0) { + Warning($"Could not create shared GL context: {SDL3.SDL_GetError()}\n"); + return null; + } + + return new SDL3_OpenGL46_Context(window, shared); + } } public unsafe class SDL3_LauncherManager : ILauncherManager, IGraphicsProvider From b0b44ecc368dc02d283838d4329539f89679533d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 13:19:15 +0300 Subject: [PATCH 05/17] Fix shadow state shader handles defaulting to index 0 --- Source.ShaderAPI.Gl46/ShaderShadow.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source.ShaderAPI.Gl46/ShaderShadow.cs b/Source.ShaderAPI.Gl46/ShaderShadow.cs index 544d6748a..930766e57 100644 --- a/Source.ShaderAPI.Gl46/ShaderShadow.cs +++ b/Source.ShaderAPI.Gl46/ShaderShadow.cs @@ -56,8 +56,8 @@ public class ShadowStateGl46 : IShaderShadow public SourceVertexSharedShadowState Vertex; public SourcePixelSharedShadowState Pixel; - public VertexShaderHandle VertexShader; - public PixelShaderHandle PixelShader; + public VertexShaderHandle VertexShader = VertexShaderHandle.INVALID; + public PixelShaderHandle PixelShader = PixelShaderHandle.INVALID; public BasePerMaterialContextData? ContextData; public ShaderUsingFlags ModulationFlags; From c4569066579bb79d0d7b9872f2e2bbbfb71f3549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 13:31:58 +0300 Subject: [PATCH 06/17] Redeclare gl_PerVertex in the clip planes shader include --- Game.Assets/sdn/shaders/common_clipplanes_gl460.vs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Game.Assets/sdn/shaders/common_clipplanes_gl460.vs b/Game.Assets/sdn/shaders/common_clipplanes_gl460.vs index b0a367a24..bf4611b7a 100644 --- a/Game.Assets/sdn/shaders/common_clipplanes_gl460.vs +++ b/Game.Assets/sdn/shaders/common_clipplanes_gl460.vs @@ -1,6 +1,11 @@ #ifndef COMMON_CLIPPLANES_GL460_VS #define COMMON_CLIPPLANES_GL460_VS +out gl_PerVertex { + vec4 gl_Position; + float gl_ClipDistance[2]; +}; + layout(std140, binding = 7) uniform source_clip_planes { vec4 clipPlanes[2]; }; From f4f5995831a35e1bddd7c35cb66bf15b5bc986ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:02:36 +0300 Subject: [PATCH 07/17] Compile shaders on a worker thread as separable programs --- Dependencies/OpenGL/Gl46.cs | 10 + Source.Common/MaterialSystem/IShaderSystem.cs | 8 + Source.ShaderAPI.Gl46/ShaderAPIGl46.cs | 81 ++--- Source.ShaderAPI.Gl46/ShaderSystem.cs | 321 ++++++++++++++++-- 4 files changed, 344 insertions(+), 76 deletions(-) diff --git a/Dependencies/OpenGL/Gl46.cs b/Dependencies/OpenGL/Gl46.cs index 007c53568..cb793241e 100644 --- a/Dependencies/OpenGL/Gl46.cs +++ b/Dependencies/OpenGL/Gl46.cs @@ -14444,6 +14444,12 @@ public static void glSpecializeShader(GLuint shader, string pEntryPoint, GLuint #endif + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void PFNGLMAXSHADERCOMPILERTHREADSKHRPROC(GLuint count); + private static PFNGLMAXSHADERCOMPILERTHREADSKHRPROC _glMaxShaderCompilerThreadsKHR; + public static bool glMaxShaderCompilerThreadsKHRLoaded => _glMaxShaderCompilerThreadsKHR != null; + public static void glMaxShaderCompilerThreadsKHR(GLuint count) => _glMaxShaderCompilerThreadsKHR(count); + /// /// Loads OpenGL functions using the specified loader delegate. /// @@ -15144,6 +15150,10 @@ public unsafe static void Import(GetProcAddressHandler loader) _glMultiDrawElementsIndirectCount = Marshal.GetDelegateForFunctionPointer(loader.Invoke("glMultiDrawElementsIndirectCount")); _glPolygonOffsetClamp = Marshal.GetDelegateForFunctionPointer(loader.Invoke("glPolygonOffsetClamp")); #endif + IntPtr maxShaderCompilerThreads = loader.Invoke("glMaxShaderCompilerThreadsKHR"); + if (maxShaderCompilerThreads == IntPtr.Zero) + maxShaderCompilerThreads = loader.Invoke("glMaxShaderCompilerThreadsARB"); + _glMaxShaderCompilerThreadsKHR = maxShaderCompilerThreads != IntPtr.Zero ? Marshal.GetDelegateForFunctionPointer(maxShaderCompilerThreads) : null!; } #endif } diff --git a/Source.Common/MaterialSystem/IShaderSystem.cs b/Source.Common/MaterialSystem/IShaderSystem.cs index 65bb0eed8..90bd6a1d9 100644 --- a/Source.Common/MaterialSystem/IShaderSystem.cs +++ b/Source.Common/MaterialSystem/IShaderSystem.cs @@ -13,6 +13,13 @@ public enum ShaderUsingFlags Editor = 0x10, } +public enum ShaderCompileState +{ + Pending, + Ready, + Failed +} + public interface IShaderSystem { public static bool IsTranslucent(IShaderShadow renderState) { @@ -33,6 +40,7 @@ public static bool IsTranslucent(IShaderShadow renderState) { void ResetShaderState(); // void CleanupRenderState(ref ShaderRenderState renderState); void DrawElements(IShader shader, IMaterialVar[] parms, IShaderShadow renderState, VertexCompressionType vertexCompression, uint materialVarTimeStamp); + void BeginFrame(); IEnumerable GetShaders(); ReadOnlySpan ShaderStateString(int i); int GetShaderAPITextureBindHandle(ITexture? texture, int v, int textureChannel); diff --git a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs index ba723304e..c0e73d6f2 100644 --- a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs +++ b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs @@ -998,16 +998,15 @@ public bool InFlashlightMode() { MeshGl46? RenderMesh; IMaterialInternal? Material; uint CombobulateShadersIfChanged() { - uint program; if (pipelineChanged) { - program = ShaderCombobulator(); + uint program = ShaderCombobulator(); if (lastShader != program) { lastShader = program; - glUseProgram(program); + glBindProgramPipeline(program); } + pipelineChanged = program == 0; } - pipelineChanged = false; return lastShader; } @@ -1015,7 +1014,8 @@ public void RenderPass() { if (IsDeactivated()) return; - CombobulateShadersIfChanged(); + if (CombobulateShadersIfChanged() == 0) + return; if (RenderMesh != null) RenderMesh.RenderPass(); @@ -1023,35 +1023,10 @@ public void RenderPass() { MeshMgr.RenderPassWithVertexAndIndexBuffers(); } - Dictionary shaderCombinations = []; - private unsafe uint ShaderCombobulator() { + private uint ShaderCombobulator() { // Determines the shader program used given the current shader handles. // If one does not exist, it is created. - Span hashedData = [activeVertexShader.Handle, activePixelShader.Handle]; - ulong hash; - fixed (nint* data = hashedData) - hash = XXH64.DigestOf(data, hashedData.Length * sizeof(nint), 0); - - if (shaderCombinations.TryGetValue(hash, out var program)) - return program; // We have already linked a program for this shader combination - - // We need to create a program then - program = glCreateProgram(); - glAttachShader(program, (uint)activeVertexShader.Handle); - glAttachShader(program, (uint)activePixelShader.Handle); - glLinkProgram(program); - // Even invalid program states should be hashed... for now. - // Maybe a time based thing for invalid programs, to try allowing for the shader developer to recover, etc... - shaderCombinations[hash] = program; - - if (!ShaderSystem.IsValidProgram(program, out string? error)) { - Warning("WARNING: Shader combobulation linker error.\n"); - Warning(error); - Warning("\n"); - return 0; - } - - return program; + return ((ShaderSystem)ShaderManager).GetProgram(activeVertexShader, activePixelShader); } private bool IsDeactivated() { @@ -1541,6 +1516,8 @@ public void GetBackBufferDimensions(out int width, out int height) { nint TextureMemoryUsedLastFrame; public void BeginFrame() { + ShaderManager.BeginFrame(); + if (ResetRenderStateNeeded) { ResetRenderState(false); ResetRenderStateNeeded = false; @@ -1824,8 +1801,21 @@ public unsafe int LocateShaderUniform(ReadOnlySpan name) { name = name[1..]; // Combobulate - uint shader = CombobulateShadersIfChanged(); + if (CombobulateShadersIfChanged() == 0) + return -1; + + ShaderSystem shaders = (ShaderSystem)ShaderManager; + int loc = LocateStageUniform(shaders.GetShaderProgram(activePixelShader), name); + if (loc != -1) + return loc; + loc = LocateStageUniform(shaders.GetShaderProgram(activeVertexShader), name); + return loc == -1 ? -1 : loc | VertexStageUniform; + } + + const int VertexStageUniform = 0x40000000; + + private unsafe int LocateStageUniform(uint shader, ReadOnlySpan name) { // Then get shader ID -> shader uniform lookup table if (!locs.TryGetValue(shader, out var lookup)) lookup = locs[shader] = []; @@ -1848,8 +1838,21 @@ public unsafe int LocateShaderUniform(ReadOnlySpan name) { uint GetCurrentProgramInternal() => CombobulateShadersIfChanged(); readonly Dictionary> uniformValues = []; - bool UniformChanged(int uniform, ReadOnlySpan values, out uint program) { - program = GetCurrentProgramInternal(); + bool UniformChanged(ref int uniform, ReadOnlySpan values, out uint program) { + program = 0; + if (uniform < 0 || GetCurrentProgramInternal() == 0) + return false; + + ShaderSystem shaders = (ShaderSystem)ShaderManager; + if ((uniform & VertexStageUniform) != 0) { + uniform &= ~VertexStageUniform; + program = shaders.GetShaderProgram(activeVertexShader); + } + else + program = shaders.GetShaderProgram(activePixelShader); + + if (program == 0) + return false; if (!uniformValues.TryGetValue(program, out var cache)) cache = uniformValues[program] = []; @@ -1865,7 +1868,7 @@ bool UniformChanged(int uniform, ReadOnlySpan values, out uint program) { } public void SetShaderUniform(int uniform, int integer) { - if (!UniformChanged(uniform, [BitConverter.Int32BitsToSingle(integer)], out uint program)) + if (!UniformChanged(ref uniform, [BitConverter.Int32BitsToSingle(integer)], out uint program)) return; #if GL_DEBUG int i = glGetError(); @@ -1878,21 +1881,21 @@ public void SetShaderUniform(int uniform, int integer) { } public void SetShaderUniform(int uniform, uint integer) { - if (!UniformChanged(uniform, [BitConverter.UInt32BitsToSingle(integer)], out uint program)) + if (!UniformChanged(ref uniform, [BitConverter.UInt32BitsToSingle(integer)], out uint program)) return; glProgramUniform1ui(program, uniform, integer); } public void SetShaderUniform(int uniform, float fl) { - if (!UniformChanged(uniform, [fl], out uint program)) + if (!UniformChanged(ref uniform, [fl], out uint program)) return; glProgramUniform1f(program, uniform, fl); } public void SetShaderUniform(int uniform, ReadOnlySpan flConsts) { - if (!UniformChanged(uniform, flConsts, out uint program)) + if (!UniformChanged(ref uniform, flConsts, out uint program)) return; glProgramUniform1fv(program, uniform, flConsts); diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index b228c9b93..ebb8d73ff 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -1,14 +1,17 @@ using Microsoft.Extensions.DependencyInjection; using Source.Common.Filesystem; +using Source.Common.Launcher; using Source.Common.MaterialSystem; using Source.Common.ShaderAPI; using Source.Common.ShaderLib; using Source.MaterialSystem; +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; +using System.Runtime.InteropServices; using System.Text; namespace Source.ShaderAPI.Gl46; @@ -17,6 +20,37 @@ public interface IShaderSystemInternal : IShaderInit, IShaderSystem; public readonly record struct ShaderCombo(string Name, int Min, int Range); +internal sealed class ShaderObject(int glType, string name, string defines, string typeName) +{ + public readonly int GLType = glType; + public readonly string Name = name; + public readonly string Defines = defines; + public readonly string TypeName = typeName; + public uint Handle; + public ShaderCompileState State; + public ShaderCompileJob? Job; +} + +internal sealed class ShaderProgram +{ + public uint Handle; + public ShaderCompileState State; +} + +internal sealed class ShaderCompileJob +{ + public required ShaderObject Shader; + public uint ShaderHandle; + public uint Handle; + public string? Error; + public List? SourceFiles; + public bool LinkFailed; + public bool Taken; + public bool HighPriority; +} + +internal sealed record ShaderCompileBatch(ShaderCompileJob[] Jobs, nint Fence); + public class ShaderSystem : IShaderSystemInternal { List ShaderDLLs = []; @@ -486,48 +520,269 @@ private void ProcessSource(StringBuilder sb, string source, int fileIndex, int s return Encoding.ASCII.GetBytes(source); } - private unsafe uint CompileShader(int glType, ReadOnlySpan name, ReadOnlySpan defines, string typeName) { + private byte[]? LoadShaderSource(string name, string defines, List sourceFiles) { using IFileHandle? handle = FileSystem.Open($"shaders/{name}", FileOpenOptions.Read, "game"); - List sourceFiles = []; - byte[]? built; if (handle == null) { - if ((built = LoadVcs(name)) == null) - return 0; - sourceFiles.Add(new(name)); + byte[]? vcs = LoadVcs(name); + if (vcs != null) + sourceFiles.Add(name); + return vcs; } - else { - Span source = stackalloc byte[(int)handle.Stream.Length]; - handle.Stream.Read(source); - built = BuildShaderSource(source, defines, new string(name), sourceFiles); - if (built == null) - return 0; - } + byte[] source = new byte[handle.Stream.Length]; + handle.Stream.ReadExactly(source); + return BuildShaderSource(source, defines, name, sourceFiles); + } + + private unsafe void CompileProgram(ShaderCompileJob job) { + ShaderObject shader = job.Shader; + job.SourceFiles = []; + byte[]? built = LoadShaderSource(shader.Name, shader.Defines, job.SourceFiles); + if (built == null) + return; - uint pShader = glCreateShader(glType); + job.ShaderHandle = glCreateShader(shader.GLType); int len = built.Length; fixed (byte* pSrc = built) - glShaderSource(pShader, 1, &pSrc, &len); - glCompileShader(pShader); + glShaderSource(job.ShaderHandle, 1, &pSrc, &len); + glCompileShader(job.ShaderHandle); + + job.Handle = glCreateProgram(); + glProgramParameteri(job.Handle, GL_PROGRAM_SEPARABLE, GL_TRUE); + glAttachShader(job.Handle, job.ShaderHandle); + glLinkProgram(job.Handle); + } + + private void SubmitCompileJob(ShaderCompileJob job) { + CompileProgram(job); + } + + private unsafe void FinishCompileJob(ShaderCompileJob job) { + if (job.Handle == 0) + return; + + if (!IsValidShader(job.ShaderHandle, out job.Error)) { + glDeleteProgram(job.Handle); + job.Handle = 0; + return; + } + + glDetachShader(job.Handle, job.ShaderHandle); + glDeleteShader(job.ShaderHandle); + + if (!IsValidProgram(job.Handle, out job.Error)) { + job.LinkFailed = true; + job.Handle = 0; + } + } + + private static void ApplyCompileJob(ShaderCompileJob job) { + ShaderObject shader = job.Shader; + shader.Handle = job.Handle; + shader.State = job.Handle != 0 ? ShaderCompileState.Ready : ShaderCompileState.Failed; + shader.Job = null; - if (!IsValidShader(pShader, out string? error)) { - Warning($"WARNING: {typeName} shader compilation error in {name}.\n"); - Warning(error); + if (job.LinkFailed) { + Warning("WARNING: Shader combobulation linker error.\n"); + Warning(job.Error!); Warning("\n"); - for (int i = 0; i < sourceFiles.Count; i++) - Warning($" [source string {i}] = {sourceFiles[i]}\n"); + } + else if (job.Error != null) { + Warning($"WARNING: {shader.TypeName} shader compilation error in {shader.Name}.\n"); + Warning(job.Error); + Warning("\n"); + for (int i = 0; i < job.SourceFiles!.Count; i++) + Warning($" [source string {i}] = {job.SourceFiles[i]}\n"); Warning("\n"); - return 0; } - else { - ReadOnlySpan combos = defines.SliceNullTerminatedString(); + else if (job.Handle != 0) { + ReadOnlySpan combos = shader.Defines.AsSpan().SliceNullTerminatedString(); if (combos.IsEmpty) - Msg($"Compiled shader: {name} ({typeName})\n"); + Msg($"Compiled shader: {shader.Name} ({shader.TypeName})\n"); else - Msg($"Compiled shader: {name} ({typeName}) [{combos}]\n"); + Msg($"Compiled shader: {shader.Name} ({shader.TypeName}) [{combos}]\n"); } + } - return pShader; + const int MaxCompileJobsPerBatch = 32; + + readonly List ShaderObjects = []; + readonly Dictionary ShaderPrograms = []; + readonly Queue PendingCompileJobs = []; + readonly Queue HighPriorityCompileJobs = []; + readonly ConcurrentQueue FinishedCompileBatches = []; + readonly List FencedCompileBatches = []; + IGraphicsContext? CompileContext; + Thread? CompileThread; + bool CompileWorkerStarted; + int OutstandingCompileJobs; + bool ParallelShaderCompile; + + private static unsafe bool HasGLExtension(ReadOnlySpan extension) { + int count; + glGetIntegerv(GL_NUM_EXTENSIONS, &count); + for (uint i = 0; i < count; i++) { + if (MemoryMarshal.CreateReadOnlySpanFromNullTerminated(glGetStringi(GL_EXTENSIONS, i)).SequenceEqual(extension)) + return true; + } + return false; + } + + private void StartCompileWorker() { + CompileWorkerStarted = true; + CompileContext = ShaderAPI.Device?.CreateSharedContext(); + if (CompileContext == null) { + Warning("Shader compile worker unavailable, compiling shaders on the main thread\n"); + return; + } + + ParallelShaderCompile = glMaxShaderCompilerThreadsKHRLoaded && (HasGLExtension("GL_KHR_parallel_shader_compile"u8) || HasGLExtension("GL_ARB_parallel_shader_compile"u8)); + CompileThread = new Thread(CompileWorker) { Name = "ShaderCompile", IsBackground = true }; + CompileThread.Start(); + } + + private unsafe void CompileWorker() { + CompileContext!.MakeCurrent(); + if (ParallelShaderCompile) + glMaxShaderCompilerThreadsKHR(2); + + List jobs = []; + while (true) { + lock (PendingCompileJobs) { + while (HighPriorityCompileJobs.Count == 0 && PendingCompileJobs.Count == 0) + Monitor.Wait(PendingCompileJobs); + TakeCompileJobs(HighPriorityCompileJobs, jobs); + TakeCompileJobs(PendingCompileJobs, jobs); + } + + if (jobs.Count == 0) + continue; + + foreach (ShaderCompileJob job in jobs) + SubmitCompileJob(job); + foreach (ShaderCompileJob job in jobs) + FinishCompileJob(job); + + ShaderCompileBatch batch = new([.. jobs], (nint)glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); + glFlush(); + FinishedCompileBatches.Enqueue(batch); + jobs.Clear(); + } + } + + private static void TakeCompileJobs(Queue queue, List jobs) { + while (queue.Count > 0 && jobs.Count < MaxCompileJobsPerBatch) { + ShaderCompileJob job = queue.Dequeue(); + if (job.Taken) + continue; + job.Taken = true; + jobs.Add(job); + } + } + + private void QueueCompileJob(ShaderCompileJob job, bool highPriority) { + if (!CompileWorkerStarted) + StartCompileWorker(); + + if (CompileContext == null) { + SubmitCompileJob(job); + FinishCompileJob(job); + ApplyCompileJob(job); + return; + } + + ++OutstandingCompileJobs; + lock (PendingCompileJobs) { + job.HighPriority = highPriority; + (highPriority ? HighPriorityCompileJobs : PendingCompileJobs).Enqueue(job); + Monitor.Pulse(PendingCompileJobs); + } + } + + internal void PrioritizeShader(nint handle) { + if (handle < 0 || handle >= ShaderObjects.Count) + return; + + ShaderObject shader = ShaderObjects[(int)handle]; + ShaderCompileJob? job = shader.Job; + if (shader.State != ShaderCompileState.Pending || job == null || job.HighPriority) + return; + + lock (PendingCompileJobs) { + if (job.Taken) + return; + job.HighPriority = true; + HighPriorityCompileJobs.Enqueue(job); + Monitor.Pulse(PendingCompileJobs); + } + } + + public void BeginFrame() => ProcessFinishedCompileJobs(); + + private unsafe void ProcessFinishedCompileJobs() { + while (FinishedCompileBatches.TryDequeue(out ShaderCompileBatch? batch)) + FencedCompileBatches.Add(batch); + + for (int i = 0; i < FencedCompileBatches.Count; i++) { + ShaderCompileBatch batch = FencedCompileBatches[i]; + if (glClientWaitSync((void*)batch.Fence, 0, 0) == GL_TIMEOUT_EXPIRED) + continue; + + glDeleteSync((void*)batch.Fence); + foreach (ShaderCompileJob job in batch.Jobs) + ApplyCompileJob(job); + OutstandingCompileJobs -= batch.Jobs.Length; + FencedCompileBatches.RemoveAt(i--); + } + } + + private nint QueueShader(int glType, ReadOnlySpan name, ReadOnlySpan defines, string typeName) { + ShaderObject shader = new(glType, new(name), new(defines), typeName); + int index = ShaderObjects.Count; + ShaderObjects.Add(shader); + shader.Job = new() { Shader = shader }; + QueueCompileJob(shader.Job, false); + return index; + } + + internal uint GetShaderProgram(nint handle) { + if (handle < 0 || handle >= ShaderObjects.Count) + return 0; + return ShaderObjects[(int)handle].Handle; + } + + internal unsafe uint GetProgram(VertexShaderHandle vertexShader, PixelShaderHandle pixelShader) { + if (!vertexShader.IsValid() || !pixelShader.IsValid()) + return 0; + + ulong key = ((ulong)(uint)vertexShader.Handle << 32) | (uint)pixelShader.Handle; + if (!ShaderPrograms.TryGetValue(key, out ShaderProgram? program)) { + // Even invalid program states should be hashed... for now. + // Maybe a time based thing for invalid programs, to try allowing for the shader developer to recover, etc... + program = new(); + ShaderPrograms[key] = program; + } + + if (program.State == ShaderCompileState.Pending) { + ShaderObject vertex = ShaderObjects[(int)vertexShader.Handle]; + ShaderObject pixel = ShaderObjects[(int)pixelShader.Handle]; + if (vertex.State == ShaderCompileState.Failed || pixel.State == ShaderCompileState.Failed) + program.State = ShaderCompileState.Failed; + else if (vertex.State == ShaderCompileState.Ready && pixel.State == ShaderCompileState.Ready) { + uint pipeline; + glCreateProgramPipelines(1, &pipeline); + glUseProgramStages(pipeline, (uint)GL_VERTEX_SHADER_BIT, vertex.Handle); + glUseProgramStages(pipeline, (uint)GL_FRAGMENT_SHADER_BIT, pixel.Handle); + program.Handle = pipeline; + program.State = ShaderCompileState.Ready; + } + else { + PrioritizeShader(vertexShader.Handle); + PrioritizeShader(pixelShader.Handle); + } + } + + return program.Handle; } Dictionary Static, List Dynamic)> comboCache = []; @@ -602,11 +857,7 @@ public VertexShaderHandle LoadVertexShader(ReadOnlySpan name, ReadOnlySpan if (vshs.TryGetValue(symbol, out VertexShaderHandle value)) return value; - uint pShader = CompileShader(GL_VERTEX_SHADER, name, defines, "Vertex"); - if (pShader == 0) - return VertexShaderHandle.INVALID; - - VertexShaderHandle vsh = new((nint)pShader); + VertexShaderHandle vsh = new(QueueShader(GL_VERTEX_SHADER, name, defines, "Vertex")); vshs[symbol] = vsh; return vsh; } @@ -616,11 +867,7 @@ public PixelShaderHandle LoadPixelShader(ReadOnlySpan name, ReadOnlySpan Date: Tue, 13 Oct 2026 14:27:09 +0300 Subject: [PATCH 08/17] Cache compiled shader programs on disk --- Source.ShaderAPI.Gl46/ShaderSystem.cs | 93 ++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index ebb8d73ff..b84da45c8 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -7,6 +7,7 @@ using Source.Common.ShaderLib; using Source.MaterialSystem; +using System.Buffers.Binary; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -44,6 +45,8 @@ internal sealed class ShaderCompileJob public uint Handle; public string? Error; public List? SourceFiles; + public string? CachePath; + public bool FromCache; public bool LinkFailed; public bool Taken; public bool HighPriority; @@ -534,6 +537,53 @@ private void ProcessSource(StringBuilder sb, string source, int fileIndex, int s return BuildShaderSource(source, defines, name, sourceFiles); } + private string? GetCachePath(ShaderObject shader) { + if (ProgramCacheDir == null) + return null; + + if (!ShaderSourceHashes.TryGetValue(shader.Name, out ulong sourceHash)) { + byte[]? source = LoadShaderSource(shader.Name, "", []); + sourceHash = source == null ? 0 : source.AsSpan().Hash(); + ShaderSourceHashes[shader.Name] = sourceHash; + } + + if (sourceHash == 0) + return null; + + ulong key = $"{sourceHash:x16};{shader.Defines}".AsSpan().Hash(false); + return $"{ProgramCacheDir}/{shader.Name}_{key:x16}.bin"; + } + + private unsafe bool LoadCachedProgram(ShaderCompileJob job) { + using IFileHandle? handle = FileSystem.Open(job.CachePath, FileOpenOptions.Read | FileOpenOptions.Binary, "DEFAULT_WRITE_PATH"); + if (handle == null || handle.Stream.Length <= sizeof(int)) + return false; + + byte[] data = new byte[handle.Stream.Length]; + handle.Stream.ReadExactly(data); + + job.Handle = glCreateProgram(); + glProgramParameteri(job.Handle, GL_PROGRAM_SEPARABLE, GL_TRUE); + fixed (byte* pData = data) + glProgramBinary(job.Handle, BinaryPrimitives.ReadInt32LittleEndian(data), pData + sizeof(int), data.Length - sizeof(int)); + job.FromCache = true; + return true; + } + + private unsafe void SaveCachedProgram(ShaderCompileJob job) { + int length = 0; + glGetProgramiv(job.Handle, GL_PROGRAM_BINARY_LENGTH, &length); + if (length <= 0) + return; + + byte[] data = new byte[sizeof(int) + length]; + int format = 0; + fixed (byte* pData = data) + glGetProgramBinary(job.Handle, length, &length, &format, pData + sizeof(int)); + BinaryPrimitives.WriteInt32LittleEndian(data, format); + FileSystem.WriteFile(job.CachePath, "DEFAULT_WRITE_PATH", data.AsSpan(0, sizeof(int) + length)); + } + private unsafe void CompileProgram(ShaderCompileJob job) { ShaderObject shader = job.Shader; job.SourceFiles = []; @@ -549,11 +599,17 @@ private unsafe void CompileProgram(ShaderCompileJob job) { job.Handle = glCreateProgram(); glProgramParameteri(job.Handle, GL_PROGRAM_SEPARABLE, GL_TRUE); + if (job.CachePath != null) + glProgramParameteri(job.Handle, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE); glAttachShader(job.Handle, job.ShaderHandle); glLinkProgram(job.Handle); } private void SubmitCompileJob(ShaderCompileJob job) { + job.CachePath = GetCachePath(job.Shader); + if (job.CachePath != null && LoadCachedProgram(job)) + return; + CompileProgram(job); } @@ -561,6 +617,20 @@ private unsafe void FinishCompileJob(ShaderCompileJob job) { if (job.Handle == 0) return; + if (job.FromCache) { + int status = 0; + glGetProgramiv(job.Handle, GL_LINK_STATUS, &status); + if (status == GL_TRUE) + return; + + glDeleteProgram(job.Handle); + job.Handle = 0; + job.FromCache = false; + CompileProgram(job); + if (job.Handle == 0) + return; + } + if (!IsValidShader(job.ShaderHandle, out job.Error)) { glDeleteProgram(job.Handle); job.Handle = 0; @@ -573,7 +643,11 @@ private unsafe void FinishCompileJob(ShaderCompileJob job) { if (!IsValidProgram(job.Handle, out job.Error)) { job.LinkFailed = true; job.Handle = 0; + return; } + + if (job.CachePath != null) + SaveCachedProgram(job); } private static void ApplyCompileJob(ShaderCompileJob job) { @@ -595,7 +669,7 @@ private static void ApplyCompileJob(ShaderCompileJob job) { Warning($" [source string {i}] = {job.SourceFiles[i]}\n"); Warning("\n"); } - else if (job.Handle != 0) { + else if (job.Handle != 0 && !job.FromCache) { ReadOnlySpan combos = shader.Defines.AsSpan().SliceNullTerminatedString(); if (combos.IsEmpty) Msg($"Compiled shader: {shader.Name} ({shader.TypeName})\n"); @@ -608,6 +682,8 @@ private static void ApplyCompileJob(ShaderCompileJob job) { readonly List ShaderObjects = []; readonly Dictionary ShaderPrograms = []; + readonly Dictionary ShaderSourceHashes = []; + string? ProgramCacheDir; readonly Queue PendingCompileJobs = []; readonly Queue HighPriorityCompileJobs = []; readonly ConcurrentQueue FinishedCompileBatches = []; @@ -628,8 +704,21 @@ private static unsafe bool HasGLExtension(ReadOnlySpan extension) { return false; } - private void StartCompileWorker() { + private static unsafe string GetGLString(int name) => Encoding.ASCII.GetString(MemoryMarshal.CreateReadOnlySpanFromNullTerminated(glGetString(name))); + + private unsafe void StartCompileWorker() { CompileWorkerStarted = true; + + int binaryFormats = 0; + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &binaryFormats); + if (binaryFormats > 0) { + string driver = $"{GetGLString(GL_VENDOR)};{GetGLString(GL_RENDERER)};{GetGLString(GL_VERSION)}"; + ProgramCacheDir = $"cache/shaders/{driver.AsSpan().Hash(false):x16}"; + FileSystem.CreateDirHierarchy(ProgramCacheDir, "DEFAULT_WRITE_PATH"); + } + else + Warning("Program binaries unsupported, shader cache disabled\n"); + CompileContext = ShaderAPI.Device?.CreateSharedContext(); if (CompileContext == null) { Warning("Shader compile worker unavailable, compiling shaders on the main thread\n"); From 5e1710407cf5a1203561cf0fdf5c894e994c6018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:41:53 +0300 Subject: [PATCH 09/17] Show the loading progress dialog on every map load --- Source.Engine/Scr.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source.Engine/Scr.cs b/Source.Engine/Scr.cs index 5b9d02772..564a28d59 100644 --- a/Source.Engine/Scr.cs +++ b/Source.Engine/Scr.cs @@ -72,6 +72,7 @@ public void BeginLoadingPlaque() { CenterStringOff(); clientDll?.HudText(null); + EngineVGui!.EnabledProgressBarForNextLoad(); EngineVGui!.OnLevelLoadingStarted(); clientGlobalVariables.FrameTime = 0; From d9466e95c3b70bc879a751fc02b1fe6d1b7edae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 15:02:18 +0300 Subject: [PATCH 10/17] Load all shader combos during map load --- Source.Common/MaterialSystem/IShaderSystem.cs | 2 ++ Source.Engine/CL.cs | 14 ++++++++ Source.ShaderAPI.Gl46/ShaderShadow.cs | 29 +++++++++------- Source.ShaderAPI.Gl46/ShaderSystem.cs | 33 +++++++++++++++++++ 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/Source.Common/MaterialSystem/IShaderSystem.cs b/Source.Common/MaterialSystem/IShaderSystem.cs index 90bd6a1d9..00fd8d836 100644 --- a/Source.Common/MaterialSystem/IShaderSystem.cs +++ b/Source.Common/MaterialSystem/IShaderSystem.cs @@ -41,6 +41,8 @@ public static bool IsTranslucent(IShaderShadow renderState) { // void CleanupRenderState(ref ShaderRenderState renderState); void DrawElements(IShader shader, IMaterialVar[] parms, IShaderShadow renderState, VertexCompressionType vertexCompression, uint materialVarTimeStamp); void BeginFrame(); + void QueueAllShaderCombos(); + void WaitForPendingCompiles(Action? progress); IEnumerable GetShaders(); ReadOnlySpan ShaderStateString(int i); int GetShaderAPITextureBindHandle(ITexture? texture, int v, int textureChannel); diff --git a/Source.Engine/CL.cs b/Source.Engine/CL.cs index 208db0eb5..45c9bc594 100644 --- a/Source.Engine/CL.cs +++ b/Source.Engine/CL.cs @@ -9,6 +9,7 @@ using Source.Common.Commands; using Source.Common.Engine; using Source.Common.Launcher; +using Source.Common.MaterialSystem; using Source.Common.Networking; using Source.Engine.Client; using Source.Engine.Server; @@ -224,6 +225,19 @@ public void FullyConnected() { // Level init post entity clientDLL.LevelInitPostEntity(); +#if !SWDS + IShaderSystem shaderSystem = Singleton(); + shaderSystem.QueueAllShaderCombos(); + int lastShaderPercent = -1; + shaderSystem.WaitForPendingCompiles((done, total) => { + int percent = done * 100 / total; + if (percent == lastShaderPercent) + return; + lastShaderPercent = percent; + EngineVGui?.UpdateCustomProgressBar((float)done / total, "#LoadingProgress_LoadResources"); + }); +#endif + // Start notifying dependencies uint ip = cl.NetChannel!.GetRemoteAddress()!.GetIPHostByteOrder(); short port = cl.NetChannel.GetRemoteAddress()!.GetPort(); diff --git a/Source.ShaderAPI.Gl46/ShaderShadow.cs b/Source.ShaderAPI.Gl46/ShaderShadow.cs index 930766e57..c52df59ae 100644 --- a/Source.ShaderAPI.Gl46/ShaderShadow.cs +++ b/Source.ShaderAPI.Gl46/ShaderShadow.cs @@ -440,6 +440,15 @@ internal sealed class ShaderComboState(IShaderSystemInternal shaders, ShaderType readonly StringBuilder defines = new(256); public nint SetShader(string fileName, int staticIndex) { + Configure(fileName, staticIndex); + ((ShaderSystem)shaders).RegisterStaticCombo(type, fileName, staticIndex); + + variants.Clear(); + + return GetVariant(0); + } + + public void Configure(string fileName, int staticIndex) { file = fileName; staticComboIndex = staticIndex; @@ -450,12 +459,10 @@ public nint SetShader(string fileName, int staticIndex) { staticComboScales = ComputeComboScales(staticCombos, out _); dynamicComboScales = ComputeComboScales(dynamicCombos, out numDynamicCombos); - - variants.Clear(); - - return GetVariant(0); } + public int DynamicComboCount => numDynamicCombos; + private static int[] ComputeComboScales(ShaderCombo[] combos, out int total) { int[] scales = new int[combos.Length]; total = 1; @@ -498,22 +505,22 @@ private static void AppendComboDefines(StringBuilder defines, ShaderCombo[] comb } } - private nint Compile(int variant) { - int staticIndex = variant / numDynamicCombos; - int dynamicIndex = variant % numDynamicCombos; - + public string GetVariantDefines(int dynamicIndex) { defines.Clear(); - AppendComboDefines(defines, staticCombos, staticComboScales, staticIndex); + AppendComboDefines(defines, staticCombos, staticComboScales, staticComboIndex); AppendComboDefines(defines, dynamicCombos, dynamicComboScales, dynamicIndex); + return defines.ToString(); + } - string source = defines.ToString(); + private nint Compile(int dynamicIndex) { + string source = GetVariantDefines(dynamicIndex); return type == ShaderType.Vertex ? (nint)shaders.LoadVertexShader(file!, source) : shaders.LoadPixelShader(file!, source); } public nint GetVariant(int dynamicIndex) { int variant = staticComboIndex * numDynamicCombos + dynamicIndex; if (!variants.TryGetValue(variant, out nint handle)) { - handle = Compile(variant); + handle = Compile(dynamicIndex); variants.Add(variant, handle); } return handle; diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index b84da45c8..ec6aec8d3 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -683,6 +683,8 @@ private static void ApplyCompileJob(ShaderCompileJob job) { readonly List ShaderObjects = []; readonly Dictionary ShaderPrograms = []; readonly Dictionary ShaderSourceHashes = []; + readonly HashSet<(ShaderType Type, string File, int StaticIndex)> StaticCombos = []; + readonly List<(ShaderType Type, string File, int StaticIndex)> NewStaticCombos = []; string? ProgramCacheDir; readonly Queue PendingCompileJobs = []; readonly Queue HighPriorityCompileJobs = []; @@ -808,6 +810,16 @@ internal void PrioritizeShader(nint handle) { public void BeginFrame() => ProcessFinishedCompileJobs(); + public void WaitForPendingCompiles(Action? progress) { + int total = OutstandingCompileJobs; + while (OutstandingCompileJobs > 0) { + ProcessFinishedCompileJobs(); + progress?.Invoke(total - OutstandingCompileJobs, total); + if (OutstandingCompileJobs > 0) + Thread.Sleep(1); + } + } + private unsafe void ProcessFinishedCompileJobs() { while (FinishedCompileBatches.TryDequeue(out ShaderCompileBatch? batch)) FencedCompileBatches.Add(batch); @@ -874,6 +886,27 @@ internal unsafe uint GetProgram(VertexShaderHandle vertexShader, PixelShaderHand return program.Handle; } + internal void RegisterStaticCombo(ShaderType type, string file, int staticIndex) { + if (StaticCombos.Add((type, file, staticIndex))) + NewStaticCombos.Add((type, file, staticIndex)); + } + + public void QueueAllShaderCombos() { + foreach (var (type, file, staticIndex) in NewStaticCombos) { + ShaderComboState combos = new(this, type); + combos.Configure(file, staticIndex); + bool vertex = type == ShaderType.Vertex; + for (int i = 0; i < combos.DynamicComboCount; i++) { + string defines = combos.GetVariantDefines(i); + if (vertex) + LoadVertexShader(file, defines); + else + LoadPixelShader(file, defines); + } + } + + NewStaticCombos.Clear(); + } Dictionary Static, List Dynamic)> comboCache = []; internal (List Static, List Dynamic) GetShaderCombos(ReadOnlySpan name) { From 50d69de54ef8c6ac922a9abd022023a1e2c8235f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 15:27:34 +0300 Subject: [PATCH 11/17] Split shader source lines as spans instead of strings --- Source.ShaderAPI.Gl46/ShaderSystem.cs | 46 +++++++++++++++------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index ec6aec8d3..261cb63b8 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -460,25 +460,26 @@ internal static unsafe bool IsValidProgram(uint program, [NotNullWhen(false)] ou sb.Append("#line 2 0\n"); - ProcessSource(sb, src[(versionEnd + 1)..], 0, 2, sourceFiles, 0); + ProcessSource(sb, src.AsSpan(versionEnd + 1), 0, 2, sourceFiles, 0); return Encoding.ASCII.GetBytes(sb.ToString()); } - private void ProcessSource(StringBuilder sb, string source, int fileIndex, int startLine, List sourceFiles, int depth) { + private void ProcessSource(StringBuilder sb, ReadOnlySpan source, int fileIndex, int startLine, List sourceFiles, int depth) { if (depth > 32) { sb.Append(source); return; } int lineNo = startLine; - foreach (string line in source.Split('\n')) { - ReadOnlySpan trimmed = line.AsSpan().TrimStart(); + foreach (Range range in MemoryExtensions.Split(source, '\n')) { + ReadOnlySpan line = source[range]; + ReadOnlySpan trimmed = line.TrimStart(); if (trimmed.StartsWith("#include")) { int q1 = line.IndexOf('"'); - int q2 = q1 >= 0 ? line.IndexOf('"', q1 + 1) : -1; - if (q1 >= 0 && q2 > q1) { - string includeName = line[(q1 + 1)..q2]; + int q2 = q1 >= 0 ? line[(q1 + 1)..].IndexOf('"') : -1; + if (q2 >= 0) { + string includeName = new(line.Slice(q1 + 1, q2)); using IFileHandle? includeHandle = FileSystem.Open($"shaders/{includeName}", FileOpenOptions.Read, "game"); if (includeHandle != null) { byte[] bytes = new byte[includeHandle.Stream.Length]; @@ -923,12 +924,12 @@ public void QueueAllShaderCombos() { handle.Stream.ReadExactly(bytes); string source = Encoding.ASCII.GetString(bytes); - foreach (string rawLine in source.Split('\n')) { - string line = rawLine.Trim(); + foreach (Range lineRange in MemoryExtensions.Split(source, '\n')) { + ReadOnlySpan line = source.AsSpan(lineRange).Trim(); if (!line.StartsWith("//")) continue; - ReadOnlySpan body = line.AsSpan(2).TrimStart(); + ReadOnlySpan body = line[2..].TrimStart(); List? list = null; if (body.StartsWith("STATIC:")) @@ -941,20 +942,25 @@ public void QueueAllShaderCombos() { int q1 = line.IndexOf('"'); if (q1 < 0) continue; - int q2 = line.IndexOf('"', q1 + 1); + ReadOnlySpan rest = line[(q1 + 1)..]; + int q2 = rest.IndexOf('"'); if (q2 < 0) continue; - string comboName = line[(q1 + 1)..q2]; + string comboName = new(rest[..q2]); + rest = rest[(q2 + 1)..]; int min = 0, max = 1; - int q3 = line.IndexOf('"', q2 + 1); - int q4 = q3 < 0 ? -1 : line.IndexOf('"', q3 + 1); - if (q4 >= 0) { - ReadOnlySpan range = line[(q3 + 1)..q4]; - int dots = range.IndexOf(".."); - if (dots >= 0) { - min = int.Parse(range[..dots]); - max = int.Parse(range[(dots + 2)..]); + int q3 = rest.IndexOf('"'); + if (q3 >= 0) { + rest = rest[(q3 + 1)..]; + int q4 = rest.IndexOf('"'); + if (q4 >= 0) { + ReadOnlySpan range = rest[..q4]; + int dots = range.IndexOf(".."); + if (dots >= 0) { + min = int.Parse(range[..dots]); + max = int.Parse(range[(dots + 2)..]); + } } } From 2855ad23171ee371baeb60c9860c618e6f4f467e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 15:44:27 +0300 Subject: [PATCH 12/17] Build shader source once per file and share it across variants --- Source.ShaderAPI.Gl46/ShaderSystem.cs | 108 +++++++++++++++++--------- 1 file changed, 72 insertions(+), 36 deletions(-) diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index 261cb63b8..26577ed31 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -52,6 +52,14 @@ internal sealed class ShaderCompileJob public bool HighPriority; } +internal sealed class ShaderSource(byte[] text, int versionLength, List sourceFiles) +{ + public readonly byte[] Text = text; + public readonly int VersionLength = versionLength; + public readonly List SourceFiles = sourceFiles; + public readonly ulong Hash = text.AsSpan().Hash(); +} + internal sealed record ShaderCompileBatch(ShaderCompileJob[] Jobs, nint Fence); public class ShaderSystem : IShaderSystemInternal @@ -435,34 +443,44 @@ internal static unsafe bool IsValidProgram(uint program, [NotNullWhen(false)] ou return true; } - private byte[]? BuildShaderSource(ReadOnlySpan source, ReadOnlySpan defines, string mainName, List sourceFiles) { + private ShaderSource? BuildShaderSource(ReadOnlySpan source, string mainName) { string src = Encoding.ASCII.GetString(source); int versionEnd = src.IndexOf('\n'); if (versionEnd < 0) return null; - sourceFiles.Add(mainName); + List sourceFiles = [mainName]; StringBuilder sb = new(); sb.Append(src.AsSpan(0, versionEnd + 1)); - - ReadOnlySpan rest = defines; - while (!rest.IsEmpty) { - int sep = rest.IndexOf(';'); - ReadOnlySpan define = (sep < 0 ? rest : rest[..sep]).Trim(); - rest = sep < 0 ? default : rest[(sep + 1)..]; - if (define.IsEmpty) - continue; - sb.Append("#define "); - sb.Append(define); - sb.Append('\n'); - } - sb.Append("#line 2 0\n"); ProcessSource(sb, src.AsSpan(versionEnd + 1), 0, 2, sourceFiles, 0); - return Encoding.ASCII.GetBytes(sb.ToString()); + return new(Encoding.ASCII.GetBytes(sb.ToString()), versionEnd + 1, sourceFiles); + } + + private static byte[] BuildDefines(ReadOnlySpan defines) { + int length = 0; + foreach (Range range in MemoryExtensions.Split(defines, ';')) { + ReadOnlySpan define = defines[range].Trim(); + if (!define.IsEmpty) + length += "#define "u8.Length + define.Length + 1; + } + + byte[] bytes = new byte[length]; + Span dest = bytes; + foreach (Range range in MemoryExtensions.Split(defines, ';')) { + ReadOnlySpan define = defines[range].Trim(); + if (define.IsEmpty) + continue; + "#define "u8.CopyTo(dest); + dest = dest["#define "u8.Length..]; + dest = dest[Encoding.ASCII.GetBytes(define, dest)..]; + dest[0] = (byte)'\n'; + dest = dest[1..]; + } + return bytes; } private void ProcessSource(StringBuilder sb, ReadOnlySpan source, int fileIndex, int startLine, List sourceFiles, int depth) { @@ -524,34 +542,35 @@ private void ProcessSource(StringBuilder sb, ReadOnlySpan source, int file return Encoding.ASCII.GetBytes(source); } - private byte[]? LoadShaderSource(string name, string defines, List sourceFiles) { + private ShaderSource? LoadShaderSource(string name) { using IFileHandle? handle = FileSystem.Open($"shaders/{name}", FileOpenOptions.Read, "game"); if (handle == null) { byte[]? vcs = LoadVcs(name); - if (vcs != null) - sourceFiles.Add(name); - return vcs; + return vcs == null ? null : new(vcs, 0, [name]); } byte[] source = new byte[handle.Stream.Length]; handle.Stream.ReadExactly(source); - return BuildShaderSource(source, defines, name, sourceFiles); + return BuildShaderSource(source, name); + } + + private ShaderSource? GetShaderSource(string name) { + if (!ShaderSources.TryGetValue(name, out ShaderSource? source)) { + source = LoadShaderSource(name); + ShaderSources[name] = source; + } + return source; } private string? GetCachePath(ShaderObject shader) { if (ProgramCacheDir == null) return null; - if (!ShaderSourceHashes.TryGetValue(shader.Name, out ulong sourceHash)) { - byte[]? source = LoadShaderSource(shader.Name, "", []); - sourceHash = source == null ? 0 : source.AsSpan().Hash(); - ShaderSourceHashes[shader.Name] = sourceHash; - } - - if (sourceHash == 0) + ShaderSource? source = GetShaderSource(shader.Name); + if (source == null) return null; - ulong key = $"{sourceHash:x16};{shader.Defines}".AsSpan().Hash(false); + ulong key = $"{source.Hash:x16};{shader.Defines}".AsSpan().Hash(false); return $"{ProgramCacheDir}/{shader.Name}_{key:x16}.bin"; } @@ -587,15 +606,32 @@ private unsafe void SaveCachedProgram(ShaderCompileJob job) { private unsafe void CompileProgram(ShaderCompileJob job) { ShaderObject shader = job.Shader; - job.SourceFiles = []; - byte[]? built = LoadShaderSource(shader.Name, shader.Defines, job.SourceFiles); - if (built == null) + ShaderSource? source = GetShaderSource(shader.Name); + if (source == null) return; + job.SourceFiles = source.SourceFiles; + byte[] defines = source.VersionLength > 0 ? BuildDefines(shader.Defines) : []; + job.ShaderHandle = glCreateShader(shader.GLType); - int len = built.Length; - fixed (byte* pSrc = built) - glShaderSource(job.ShaderHandle, 1, &pSrc, &len); + fixed (byte* pText = source.Text) + fixed (byte* pDefines = defines) { + if (defines.Length == 0) { + int length = source.Text.Length; + glShaderSource(job.ShaderHandle, 1, &pText, &length); + } + else { + byte** strings = stackalloc byte*[3]; + int* lengths = stackalloc int[3]; + strings[0] = pText; + lengths[0] = source.VersionLength; + strings[1] = pDefines; + lengths[1] = defines.Length; + strings[2] = pText + source.VersionLength; + lengths[2] = source.Text.Length - source.VersionLength; + glShaderSource(job.ShaderHandle, 3, strings, lengths); + } + } glCompileShader(job.ShaderHandle); job.Handle = glCreateProgram(); @@ -683,7 +719,7 @@ private static void ApplyCompileJob(ShaderCompileJob job) { readonly List ShaderObjects = []; readonly Dictionary ShaderPrograms = []; - readonly Dictionary ShaderSourceHashes = []; + readonly Dictionary ShaderSources = []; readonly HashSet<(ShaderType Type, string File, int StaticIndex)> StaticCombos = []; readonly List<(ShaderType Type, string File, int StaticIndex)> NewStaticCombos = []; string? ProgramCacheDir; From 9dc0d1a240e23f121a34e4b077e937e7be91c040 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:31:34 -0700 Subject: [PATCH 13/17] Explicit layout in shader in/out Vertex/pixel shaders are linked as separate programs now --- Game.Assets/sdn/shaders/bloomadd_gl460.fs | 2 +- Game.Assets/sdn/shaders/blurfilter_gl460.fs | 14 ++++++------- Game.Assets/sdn/shaders/blurfilter_gl460.vs | 14 ++++++------- Game.Assets/sdn/shaders/downsample_gl460.vs | 8 ++++---- .../sdn/shaders/downsample_nohdr_gl460.fs | 8 ++++---- Game.Assets/sdn/shaders/eyeglint_gl460.fs | 6 +++--- Game.Assets/sdn/shaders/eyeglint_gl460.vs | 6 +++--- Game.Assets/sdn/shaders/eyes_gl460.fs | 10 +++++----- Game.Assets/sdn/shaders/eyes_gl460.vs | 10 +++++----- .../shaders/introscreenspaceeffect_gl460.fs | 2 +- .../sdn/shaders/lightmappedgeneric_gl460.fs | 20 +++++++++---------- .../sdn/shaders/lightmappedgeneric_gl460.vs | 20 +++++++++---------- .../sdn/shaders/luminance_compare_gl460.fs | 2 +- .../sdn/shaders/screenspaceeffect_gl460.vs | 10 +++++----- Game.Assets/sdn/shaders/shadow_gl460.fs | 12 +++++------ Game.Assets/sdn/shaders/shadow_gl460.vs | 12 +++++------ Game.Assets/sdn/shaders/shadowmodel_gl460.fs | 10 +++++----- Game.Assets/sdn/shaders/shadowmodel_gl460.vs | 10 +++++----- Game.Assets/sdn/shaders/skin_gl460.fs | 12 +++++------ Game.Assets/sdn/shaders/skin_gl460.vs | 12 +++++------ Game.Assets/sdn/shaders/sky_gl460.fs | 2 +- Game.Assets/sdn/shaders/sky_gl460.vs | 10 +++++----- .../sdn/shaders/sky_hdr_compressed_gl460.fs | 2 +- .../shaders/sky_hdr_compressed_rgbs_gl460.fs | 10 +++++----- Game.Assets/sdn/shaders/teeth_bump_gl460.fs | 12 +++++------ Game.Assets/sdn/shaders/teeth_bump_gl460.vs | 12 +++++------ Game.Assets/sdn/shaders/teeth_gl460.fs | 6 +++--- Game.Assets/sdn/shaders/teeth_gl460.vs | 6 +++--- Game.Assets/sdn/shaders/unlitgeneric_gl460.fs | 4 ++-- Game.Assets/sdn/shaders/unlitgeneric_gl460.vs | 4 ++-- .../sdn/shaders/unlittwotexture_gl460.fs | 6 +++--- .../sdn/shaders/unlittwotexture_gl460.vs | 6 +++--- .../shaders/vertexlitgeneric_bump_gl460.fs | 16 +++++++-------- .../shaders/vertexlitgeneric_bump_gl460.vs | 16 +++++++-------- .../sdn/shaders/vertexlitgeneric_gl460.fs | 20 +++++++++---------- .../sdn/shaders/vertexlitgeneric_gl460.vs | 20 +++++++++---------- Game.Assets/sdn/shaders/water_gl460.fs | 16 +++++++-------- Game.Assets/sdn/shaders/water_gl460.vs | 16 +++++++-------- Game.Assets/sdn/shaders/watercheap_gl460.fs | 10 +++++----- Game.Assets/sdn/shaders/watercheap_gl460.vs | 10 +++++----- .../shaders/worldvertextransition_gl460.fs | 6 +++--- .../shaders/worldvertextransition_gl460.vs | 6 +++--- 42 files changed, 208 insertions(+), 208 deletions(-) diff --git a/Game.Assets/sdn/shaders/bloomadd_gl460.fs b/Game.Assets/sdn/shaders/bloomadd_gl460.fs index f90ea2ef0..e3745b9eb 100644 --- a/Game.Assets/sdn/shaders/bloomadd_gl460.fs +++ b/Game.Assets/sdn/shaders/bloomadd_gl460.fs @@ -1,6 +1,6 @@ #version 460 -in vec2 vs_BaseTexCoord; +layout(location = 0) in vec2 vs_BaseTexCoord; layout(std140, binding = 3) uniform source_pixel_sharedUBO { bool isAlphaTesting; diff --git a/Game.Assets/sdn/shaders/blurfilter_gl460.fs b/Game.Assets/sdn/shaders/blurfilter_gl460.fs index 00b0d6fca..ab81a084d 100644 --- a/Game.Assets/sdn/shaders/blurfilter_gl460.fs +++ b/Game.Assets/sdn/shaders/blurfilter_gl460.fs @@ -1,12 +1,12 @@ #version 460 -in vec2 vs_CoordTap0; -in vec2 vs_CoordTap1; -in vec2 vs_CoordTap2; -in vec2 vs_CoordTap3; -in vec2 vs_CoordTap1Neg; -in vec2 vs_CoordTap2Neg; -in vec2 vs_CoordTap3Neg; +layout(location = 0) in vec2 vs_CoordTap0; +layout(location = 1) in vec2 vs_CoordTap1; +layout(location = 2) in vec2 vs_CoordTap2; +layout(location = 3) in vec2 vs_CoordTap3; +layout(location = 4) in vec2 vs_CoordTap1Neg; +layout(location = 5) in vec2 vs_CoordTap2Neg; +layout(location = 6) in vec2 vs_CoordTap3Neg; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/blurfilter_gl460.vs b/Game.Assets/sdn/shaders/blurfilter_gl460.vs index cd39e0a6a..fdd6a0136 100644 --- a/Game.Assets/sdn/shaders/blurfilter_gl460.vs +++ b/Game.Assets/sdn/shaders/blurfilter_gl460.vs @@ -11,13 +11,13 @@ layout(std140, binding = 5) uniform source_vs_constants { #define vsTapOffs1 vs_const[49].xy #define vsTapOffs2 vs_const[50].xy -out vec2 vs_CoordTap0; -out vec2 vs_CoordTap1; -out vec2 vs_CoordTap2; -out vec2 vs_CoordTap3; -out vec2 vs_CoordTap1Neg; -out vec2 vs_CoordTap2Neg; -out vec2 vs_CoordTap3Neg; +layout(location = 0) out vec2 vs_CoordTap0; +layout(location = 1) out vec2 vs_CoordTap1; +layout(location = 2) out vec2 vs_CoordTap2; +layout(location = 3) out vec2 vs_CoordTap3; +layout(location = 4) out vec2 vs_CoordTap1Neg; +layout(location = 5) out vec2 vs_CoordTap2Neg; +layout(location = 6) out vec2 vs_CoordTap3Neg; vec2 FlipV(vec2 uv) { diff --git a/Game.Assets/sdn/shaders/downsample_gl460.vs b/Game.Assets/sdn/shaders/downsample_gl460.vs index 43b5f1873..48ad038e2 100644 --- a/Game.Assets/sdn/shaders/downsample_gl460.vs +++ b/Game.Assets/sdn/shaders/downsample_gl460.vs @@ -12,10 +12,10 @@ layout(std140, binding = 5) uniform source_vs_constants { #define vsTapOffs2 vs_const[50].xy #define vsTapOffs3 vs_const[51].xy -out vec2 vs_CoordTap0; -out vec2 vs_CoordTap1; -out vec2 vs_CoordTap2; -out vec2 vs_CoordTap3; +layout(location = 0) out vec2 vs_CoordTap0; +layout(location = 1) out vec2 vs_CoordTap1; +layout(location = 2) out vec2 vs_CoordTap2; +layout(location = 3) out vec2 vs_CoordTap3; vec2 FlipV(vec2 uv) { diff --git a/Game.Assets/sdn/shaders/downsample_nohdr_gl460.fs b/Game.Assets/sdn/shaders/downsample_nohdr_gl460.fs index 18e4d3bb3..eec65a2aa 100644 --- a/Game.Assets/sdn/shaders/downsample_nohdr_gl460.fs +++ b/Game.Assets/sdn/shaders/downsample_nohdr_gl460.fs @@ -1,10 +1,10 @@ #version 460 // STATIC: "CSTRIKE" "0..1" -in vec2 vs_CoordTap0; -in vec2 vs_CoordTap1; -in vec2 vs_CoordTap2; -in vec2 vs_CoordTap3; +layout(location = 0) in vec2 vs_CoordTap0; +layout(location = 1) in vec2 vs_CoordTap1; +layout(location = 2) in vec2 vs_CoordTap2; +layout(location = 3) in vec2 vs_CoordTap3; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/eyeglint_gl460.fs b/Game.Assets/sdn/shaders/eyeglint_gl460.fs index c309f80c3..5c4b325e5 100644 --- a/Game.Assets/sdn/shaders/eyeglint_gl460.fs +++ b/Game.Assets/sdn/shaders/eyeglint_gl460.fs @@ -1,8 +1,8 @@ #version 460 -in vec2 vs_TexCoord; -in vec2 vs_GlintCenter; -in vec3 vs_GlintColor; +layout(location = 0) in vec2 vs_TexCoord; +layout(location = 1) in vec2 vs_GlintCenter; +layout(location = 2) in vec3 vs_GlintColor; out vec4 fragColor; diff --git a/Game.Assets/sdn/shaders/eyeglint_gl460.vs b/Game.Assets/sdn/shaders/eyeglint_gl460.vs index a469a3265..16db7d4fd 100644 --- a/Game.Assets/sdn/shaders/eyeglint_gl460.vs +++ b/Game.Assets/sdn/shaders/eyeglint_gl460.vs @@ -5,9 +5,9 @@ layout(location = 10) in vec2 v_TexCoord0; layout(location = 11) in vec2 v_TexCoord1; layout(location = 12) in vec3 v_TexCoord2; -out vec2 vs_TexCoord; -out vec2 vs_GlintCenter; -out vec3 vs_GlintColor; +layout(location = 0) out vec2 vs_TexCoord; +layout(location = 1) out vec2 vs_GlintCenter; +layout(location = 2) out vec3 vs_GlintColor; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/eyes_gl460.fs b/Game.Assets/sdn/shaders/eyes_gl460.fs index 4d9636c5f..bd03e869d 100644 --- a/Game.Assets/sdn/shaders/eyes_gl460.fs +++ b/Game.Assets/sdn/shaders/eyes_gl460.fs @@ -2,11 +2,11 @@ // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" // DYNAMIC: "PIXELFOGTYPE" "0..2" -in vec2 vs_BaseTexCoord; -in vec2 vs_IrisTexCoord; -in vec2 vs_GlintTexCoord; -in vec3 vs_Color; -in vec4 vs_WorldPos_ProjPosZ; +layout(location = 0) in vec2 vs_BaseTexCoord; +layout(location = 1) in vec2 vs_IrisTexCoord; +layout(location = 2) in vec2 vs_GlintTexCoord; +layout(location = 3) in vec3 vs_Color; +layout(location = 4) in vec4 vs_WorldPos_ProjPosZ; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/eyes_gl460.vs b/Game.Assets/sdn/shaders/eyes_gl460.vs index db4a29985..7b4a47f2d 100644 --- a/Game.Assets/sdn/shaders/eyes_gl460.vs +++ b/Game.Assets/sdn/shaders/eyes_gl460.vs @@ -53,11 +53,11 @@ const bool g_bHalfLambert = HALFLAMBERT != 0; #define modelOrigin const4.xyz #endif -out vec2 vs_BaseTexCoord; -out vec2 vs_IrisTexCoord; -out vec2 vs_GlintTexCoord; -out vec3 vs_Color; -out vec4 vs_WorldPos_ProjPosZ; +layout(location = 0) out vec2 vs_BaseTexCoord; +layout(location = 1) out vec2 vs_IrisTexCoord; +layout(location = 2) out vec2 vs_GlintTexCoord; +layout(location = 3) out vec3 vs_Color; +layout(location = 4) out vec4 vs_WorldPos_ProjPosZ; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/introscreenspaceeffect_gl460.fs b/Game.Assets/sdn/shaders/introscreenspaceeffect_gl460.fs index 1281bbc21..32f3ebb95 100644 --- a/Game.Assets/sdn/shaders/introscreenspaceeffect_gl460.fs +++ b/Game.Assets/sdn/shaders/introscreenspaceeffect_gl460.fs @@ -1,7 +1,7 @@ #version 460 // DYNAMIC: "MODE" "0..9" -in vec2 vs_BaseTexCoord; +layout(location = 0) in vec2 vs_BaseTexCoord; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.fs b/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.fs index 8aa32bea7..80b5c40c1 100644 --- a/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.fs +++ b/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.fs @@ -30,21 +30,21 @@ // DYNAMIC: "LIGHTING_PREVIEW" "0..1" #if SEAMLESS -in vec3 vs_SeamlessTexCoord; // zy xz -in vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; // envmap mask +layout(location = 0) in vec3 vs_SeamlessTexCoord; // zy xz +layout(location = 1) in vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; // envmap mask #else -in vec2 vs_BaseTexCoord; +layout(location = 2) in vec2 vs_BaseTexCoord; // detail textures and bumpmaps are mutually exclusive so that we have enough texcoords. -in vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; +layout(location = 1) in vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; #endif -centroid in vec4 vs_LightmapTexCoord1And2; -centroid in vec4 vs_LightmapTexCoord3; -in vec4 vs_WorldPos_ProjPosZ; +layout(location = 3) centroid in vec4 vs_LightmapTexCoord1And2; +layout(location = 4) centroid in vec4 vs_LightmapTexCoord3; +layout(location = 5) in vec4 vs_WorldPos_ProjPosZ; #if CUBEMAP || (LIGHTING_PREVIEW) -in mat3 vs_TangentSpaceTranspose; +layout(location = 6) in mat3 vs_TangentSpaceTranspose; #endif -in vec4 vs_Color; -in float vs_VertexBlendX; +layout(location = 9) in vec4 vs_Color; +layout(location = 10) in float vs_VertexBlendX; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.vs b/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.vs index 5fd1ea750..37e24ca6e 100644 --- a/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.vs +++ b/Game.Assets/sdn/shaders/lightmappedgeneric_gl460.vs @@ -63,23 +63,23 @@ const bool g_bVertexAlphaTexBlendFactor = VERTEXALPHATEXBLENDFACTOR != 0; const bool g_BumpMask = BUMPMASK != 0; #if SEAMLESS -out vec3 vs_SeamlessTexCoord; // x y z -out vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; // envmap mask +layout(location = 0) out vec3 vs_SeamlessTexCoord; // x y z +layout(location = 1) out vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; // envmap mask #else -out vec2 vs_BaseTexCoord; +layout(location = 2) out vec2 vs_BaseTexCoord; // detail textures and bumpmaps are mutually exclusive so that we have enough texcoords. -out vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; +layout(location = 1) out vec4 vs_DetailOrBumpAndEnvmapMaskTexCoord; #endif -centroid out vec4 vs_LightmapTexCoord1And2; -centroid out vec4 vs_LightmapTexCoord3; // and basetexcoord*mask_scale -out vec4 vs_WorldPos_ProjPosZ; +layout(location = 3) centroid out vec4 vs_LightmapTexCoord1And2; +layout(location = 4) centroid out vec4 vs_LightmapTexCoord3; // and basetexcoord*mask_scale +layout(location = 5) out vec4 vs_WorldPos_ProjPosZ; #if TANGENTSPACE || (LIGHTING_PREVIEW) -out mat3 vs_TangentSpaceTranspose; +layout(location = 6) out mat3 vs_TangentSpaceTranspose; #endif -out vec4 vs_Color; // in seamless, r g b = blend weights -out float vs_VertexBlendX; +layout(location = 9) out vec4 vs_Color; // in seamless, r g b = blend weights +layout(location = 10) out float vs_VertexBlendX; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/luminance_compare_gl460.fs b/Game.Assets/sdn/shaders/luminance_compare_gl460.fs index b41febbdc..6145c3b35 100644 --- a/Game.Assets/sdn/shaders/luminance_compare_gl460.fs +++ b/Game.Assets/sdn/shaders/luminance_compare_gl460.fs @@ -1,6 +1,6 @@ #version 460 -in vec2 vs_BaseTexCoord; +layout(location = 0) in vec2 vs_BaseTexCoord; layout(std140, binding = 3) uniform source_pixel_sharedUBO { bool isAlphaTesting; diff --git a/Game.Assets/sdn/shaders/screenspaceeffect_gl460.vs b/Game.Assets/sdn/shaders/screenspaceeffect_gl460.vs index f11f978a7..a117fb4a6 100644 --- a/Game.Assets/sdn/shaders/screenspaceeffect_gl460.vs +++ b/Game.Assets/sdn/shaders/screenspaceeffect_gl460.vs @@ -12,11 +12,11 @@ layout(std140, binding = 0) uniform source_matrices { mat4 modelMatrix; }; -out vec2 vs_BaseTexCoord; -out vec4 vs_TexCoord0; -out vec4 vs_TexCoord1; -out vec4 vs_TexCoord2; -out vec4 vs_Color0; +layout(location = 0) out vec2 vs_BaseTexCoord; +layout(location = 1) out vec4 vs_TexCoord0; +layout(location = 2) out vec4 vs_TexCoord1; +layout(location = 3) out vec4 vs_TexCoord2; +layout(location = 4) out vec4 vs_Color0; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/shadow_gl460.fs b/Game.Assets/sdn/shaders/shadow_gl460.fs index 88a289cfc..525fc5dff 100644 --- a/Game.Assets/sdn/shaders/shadow_gl460.fs +++ b/Game.Assets/sdn/shaders/shadow_gl460.fs @@ -1,11 +1,11 @@ #version 460 -centroid in vec2 vs_TexCoord0; -centroid in vec2 vs_TexCoord1; -centroid in vec2 vs_TexCoord2; -centroid in vec2 vs_TexCoord3; -centroid in vec2 vs_TexCoord4; -in vec4 vs_ShadowColor; +layout(location = 0) centroid in vec2 vs_TexCoord0; +layout(location = 1) centroid in vec2 vs_TexCoord1; +layout(location = 2) centroid in vec2 vs_TexCoord2; +layout(location = 3) centroid in vec2 vs_TexCoord3; +layout(location = 4) centroid in vec2 vs_TexCoord4; +layout(location = 5) in vec4 vs_ShadowColor; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/shadow_gl460.vs b/Game.Assets/sdn/shaders/shadow_gl460.vs index a9a81d5d0..1971a8f78 100644 --- a/Game.Assets/sdn/shaders/shadow_gl460.vs +++ b/Game.Assets/sdn/shaders/shadow_gl460.vs @@ -19,12 +19,12 @@ layout(std140, binding = 5) uniform source_vs_constants { #define cTextureJitter0 vs_const[50] #define cTextureJitter1 vs_const[51] -centroid out vec2 vs_TexCoord0; -centroid out vec2 vs_TexCoord1; -centroid out vec2 vs_TexCoord2; -centroid out vec2 vs_TexCoord3; -centroid out vec2 vs_TexCoord4; -out vec4 vs_ShadowColor; +layout(location = 0) centroid out vec2 vs_TexCoord0; +layout(location = 1) centroid out vec2 vs_TexCoord1; +layout(location = 2) centroid out vec2 vs_TexCoord2; +layout(location = 3) centroid out vec2 vs_TexCoord3; +layout(location = 4) centroid out vec2 vs_TexCoord4; +layout(location = 5) out vec4 vs_ShadowColor; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/shadowmodel_gl460.fs b/Game.Assets/sdn/shaders/shadowmodel_gl460.fs index 8a8c51367..47ec56b02 100644 --- a/Game.Assets/sdn/shaders/shadowmodel_gl460.fs +++ b/Game.Assets/sdn/shaders/shadowmodel_gl460.fs @@ -1,10 +1,10 @@ #version 460 -in vec3 vs_T0; -in vec3 vs_T1; -in vec3 vs_T2; -in float vs_T3; -in vec4 vs_Color; +layout(location = 0) in vec3 vs_T0; +layout(location = 1) in vec3 vs_T1; +layout(location = 2) in vec3 vs_T2; +layout(location = 3) in float vs_T3; +layout(location = 4) in vec4 vs_Color; layout(binding = 0) uniform sampler2D basetexture; diff --git a/Game.Assets/sdn/shaders/shadowmodel_gl460.vs b/Game.Assets/sdn/shaders/shadowmodel_gl460.vs index 187ac37ec..568fdb4f2 100644 --- a/Game.Assets/sdn/shaders/shadowmodel_gl460.vs +++ b/Game.Assets/sdn/shaders/shadowmodel_gl460.vs @@ -25,11 +25,11 @@ layout(std140, binding = 5) uniform source_vs_constants { #define flOneOverShadowDist cShadowConstants.y #define flShadowScale cShadowConstants.z -out vec3 vs_T0; -out vec3 vs_T1; -out vec3 vs_T2; -out float vs_T3; -out vec4 vs_Color; +layout(location = 0) out vec3 vs_T0; +layout(location = 1) out vec3 vs_T1; +layout(location = 2) out vec3 vs_T2; +layout(location = 3) out float vs_T3; +layout(location = 4) out vec4 vs_Color; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/skin_gl460.fs b/Game.Assets/sdn/shaders/skin_gl460.fs index 55403a0cc..c595f63e5 100644 --- a/Game.Assets/sdn/shaders/skin_gl460.fs +++ b/Game.Assets/sdn/shaders/skin_gl460.fs @@ -19,12 +19,12 @@ // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" // DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" -in vec4 vs_BaseTexCoord; // xy=base zw=detail -in vec3 vs_LightAtten; // Scalar light attenuation factors for FOUR lights -in vec3 vs_WorldVertToEyeVector; -in mat3 vs_TangentSpaceTranspose; -in vec4 vs_WorldPos_Atten3; -in vec4 vs_ProjPos_WrinkleWeight; +layout(location = 0) in vec4 vs_BaseTexCoord; // xy=base zw=detail +layout(location = 1) in vec3 vs_LightAtten; // Scalar light attenuation factors for FOUR lights +layout(location = 2) in vec3 vs_WorldVertToEyeVector; +layout(location = 3) in mat3 vs_TangentSpaceTranspose; +layout(location = 6) in vec4 vs_WorldPos_Atten3; +layout(location = 7) in vec4 vs_ProjPos_WrinkleWeight; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/skin_gl460.vs b/Game.Assets/sdn/shaders/skin_gl460.vs index 006c3995f..7da2acbd4 100644 --- a/Game.Assets/sdn/shaders/skin_gl460.vs +++ b/Game.Assets/sdn/shaders/skin_gl460.vs @@ -39,12 +39,12 @@ const int SHADER_SPECIFIC_CONST_4 = 52; const bool g_bSkinning = SKINNING != 0; -out vec4 vs_BaseTexCoord; // includes detail tex coord -out vec3 vs_LightAtten; -out vec3 vs_WorldVertToEyeVector; -out mat3 vs_TangentSpaceTranspose; -out vec4 vs_WorldPos_Atten3; -out vec4 vs_ProjPos_WrinkleWeight; +layout(location = 0) out vec4 vs_BaseTexCoord; // includes detail tex coord +layout(location = 1) out vec3 vs_LightAtten; +layout(location = 2) out vec3 vs_WorldVertToEyeVector; +layout(location = 3) out mat3 vs_TangentSpaceTranspose; +layout(location = 6) out vec4 vs_WorldPos_Atten3; +layout(location = 7) out vec4 vs_ProjPos_WrinkleWeight; //----------------------------------------------------------------------------- // Main shader entry point diff --git a/Game.Assets/sdn/shaders/sky_gl460.fs b/Game.Assets/sdn/shaders/sky_gl460.fs index 29a1cda31..d76fa0d03 100644 --- a/Game.Assets/sdn/shaders/sky_gl460.fs +++ b/Game.Assets/sdn/shaders/sky_gl460.fs @@ -1,7 +1,7 @@ #version 460 // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" -in vec2 vs_BaseTexCoord00; +layout(location = 0) in vec2 vs_BaseTexCoord00; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/sky_gl460.vs b/Game.Assets/sdn/shaders/sky_gl460.vs index edc41bef7..d522d04f0 100644 --- a/Game.Assets/sdn/shaders/sky_gl460.vs +++ b/Game.Assets/sdn/shaders/sky_gl460.vs @@ -22,11 +22,11 @@ layout(std140, binding = 5) uniform source_vs_constants { #define U_TO_PIXEL_COORD_SCALE (g_vTextureSizeInfo.z) #define V_TO_PIXEL_COORD_SCALE (g_vTextureSizeInfo.w) -out vec2 vs_BaseTexCoord00; -out vec2 vs_BaseTexCoord01; -out vec2 vs_BaseTexCoord10; -out vec2 vs_BaseTexCoord11; -out vec2 vs_BaseTexCoord_In_Pixels; +layout(location = 0) out vec2 vs_BaseTexCoord00; +layout(location = 1) out vec2 vs_BaseTexCoord01; +layout(location = 2) out vec2 vs_BaseTexCoord10; +layout(location = 3) out vec2 vs_BaseTexCoord11; +layout(location = 4) out vec2 vs_BaseTexCoord_In_Pixels; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/sky_hdr_compressed_gl460.fs b/Game.Assets/sdn/shaders/sky_hdr_compressed_gl460.fs index 45452511f..854b910bc 100644 --- a/Game.Assets/sdn/shaders/sky_hdr_compressed_gl460.fs +++ b/Game.Assets/sdn/shaders/sky_hdr_compressed_gl460.fs @@ -1,7 +1,7 @@ #version 460 // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" -in vec2 vs_BaseTexCoord00; +layout(location = 0) in vec2 vs_BaseTexCoord00; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/sky_hdr_compressed_rgbs_gl460.fs b/Game.Assets/sdn/shaders/sky_hdr_compressed_rgbs_gl460.fs index 3b2a6f938..480b7e6d4 100644 --- a/Game.Assets/sdn/shaders/sky_hdr_compressed_rgbs_gl460.fs +++ b/Game.Assets/sdn/shaders/sky_hdr_compressed_rgbs_gl460.fs @@ -1,11 +1,11 @@ #version 460 // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" -in vec2 vs_BaseTexCoord00; -in vec2 vs_BaseTexCoord01; -in vec2 vs_BaseTexCoord10; -in vec2 vs_BaseTexCoord11; -in vec2 vs_BaseTexCoord_In_Pixels; +layout(location = 0) in vec2 vs_BaseTexCoord00; +layout(location = 1) in vec2 vs_BaseTexCoord01; +layout(location = 2) in vec2 vs_BaseTexCoord10; +layout(location = 3) in vec2 vs_BaseTexCoord11; +layout(location = 4) in vec2 vs_BaseTexCoord_In_Pixels; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/teeth_bump_gl460.fs b/Game.Assets/sdn/shaders/teeth_bump_gl460.fs index 0e6411593..9338bced1 100644 --- a/Game.Assets/sdn/shaders/teeth_bump_gl460.fs +++ b/Game.Assets/sdn/shaders/teeth_bump_gl460.fs @@ -4,12 +4,12 @@ // DYNAMIC: "AMBIENT_LIGHT" "0..1" // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" -in vec2 vs_BaseTexCoord; -in vec4 vs_WorldVertToEyeVector_Darkening; -in mat3 vs_TangentSpace; -in vec4 vs_WorldPos_ProjPosZ; -in vec2 vs_LightAtten01; -in vec2 vs_LightAtten23; +layout(location = 0) in vec2 vs_BaseTexCoord; +layout(location = 1) in vec4 vs_WorldVertToEyeVector_Darkening; +layout(location = 2) in mat3 vs_TangentSpace; +layout(location = 5) in vec4 vs_WorldPos_ProjPosZ; +layout(location = 6) in vec2 vs_LightAtten01; +layout(location = 7) in vec2 vs_LightAtten23; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/teeth_bump_gl460.vs b/Game.Assets/sdn/shaders/teeth_bump_gl460.vs index e164626f3..aaf23f13a 100644 --- a/Game.Assets/sdn/shaders/teeth_bump_gl460.vs +++ b/Game.Assets/sdn/shaders/teeth_bump_gl460.vs @@ -42,12 +42,12 @@ const bool g_bSkinning = SKINNING != 0; #define modelOrigin const4.xyz #endif -out vec2 vs_BaseTexCoord; -out vec4 vs_WorldVertToEyeVector_Darkening; -out mat3 vs_TangentSpace; -out vec4 vs_WorldPos_ProjPosZ; -out vec2 vs_LightAtten01; -out vec2 vs_LightAtten23; +layout(location = 0) out vec2 vs_BaseTexCoord; +layout(location = 1) out vec4 vs_WorldVertToEyeVector_Darkening; +layout(location = 2) out mat3 vs_TangentSpace; +layout(location = 5) out vec4 vs_WorldPos_ProjPosZ; +layout(location = 6) out vec2 vs_LightAtten01; +layout(location = 7) out vec2 vs_LightAtten23; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/teeth_gl460.fs b/Game.Assets/sdn/shaders/teeth_gl460.fs index 068da978a..df5b25e52 100644 --- a/Game.Assets/sdn/shaders/teeth_gl460.fs +++ b/Game.Assets/sdn/shaders/teeth_gl460.fs @@ -2,9 +2,9 @@ // DYNAMIC: "PIXELFOGTYPE" "0..2" // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" -in vec2 vs_BaseTexCoord; -in vec3 vs_VertAtten; -in vec4 vs_WorldPos_ProjPosZ; +layout(location = 0) in vec2 vs_BaseTexCoord; +layout(location = 1) in vec3 vs_VertAtten; +layout(location = 2) in vec4 vs_WorldPos_ProjPosZ; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/teeth_gl460.vs b/Game.Assets/sdn/shaders/teeth_gl460.vs index 569dd4b16..b5baf9979 100644 --- a/Game.Assets/sdn/shaders/teeth_gl460.vs +++ b/Game.Assets/sdn/shaders/teeth_gl460.vs @@ -43,9 +43,9 @@ const bool g_bSkinning = SKINNING != 0; #define modelOrigin const4.xyz #endif -out vec2 vs_BaseTexCoord; -out vec3 vs_VertAtten; -out vec4 vs_WorldPos_ProjPosZ; +layout(location = 0) out vec2 vs_BaseTexCoord; +layout(location = 1) out vec3 vs_VertAtten; +layout(location = 2) out vec4 vs_WorldPos_ProjPosZ; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/unlitgeneric_gl460.fs b/Game.Assets/sdn/shaders/unlitgeneric_gl460.fs index b030de42c..89ae88206 100644 --- a/Game.Assets/sdn/shaders/unlitgeneric_gl460.fs +++ b/Game.Assets/sdn/shaders/unlitgeneric_gl460.fs @@ -1,8 +1,8 @@ #version 460 // STATIC: "BASETEXTURE" "0..1" -in vec2 vs_TexCoord; -in vec4 vs_Color; +layout(location = 0) in vec2 vs_TexCoord; +layout(location = 1) in vec4 vs_Color; layout(std140, binding = 3) uniform source_pixel_sharedUBO { bool isAlphaTesting; diff --git a/Game.Assets/sdn/shaders/unlitgeneric_gl460.vs b/Game.Assets/sdn/shaders/unlitgeneric_gl460.vs index 872ef14d8..e87532ecb 100644 --- a/Game.Assets/sdn/shaders/unlitgeneric_gl460.vs +++ b/Game.Assets/sdn/shaders/unlitgeneric_gl460.vs @@ -19,8 +19,8 @@ const int VertexAlpha = 32; uniform int flags; -out vec2 vs_TexCoord; -out vec4 vs_Color; +layout(location = 0) out vec2 vs_TexCoord; +layout(location = 1) out vec4 vs_Color; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/unlittwotexture_gl460.fs b/Game.Assets/sdn/shaders/unlittwotexture_gl460.fs index 19872c6c1..374a48a37 100644 --- a/Game.Assets/sdn/shaders/unlittwotexture_gl460.fs +++ b/Game.Assets/sdn/shaders/unlittwotexture_gl460.fs @@ -2,9 +2,9 @@ // DYNAMIC: "PIXELFOGTYPE" "0..1" // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" -in vec2 vs_BaseTexCoord; -in vec2 vs_BaseTexCoord2; -in vec4 vs_WorldPos_ProjPosZ; +layout(location = 0) in vec2 vs_BaseTexCoord; +layout(location = 1) in vec2 vs_BaseTexCoord2; +layout(location = 2) in vec4 vs_WorldPos_ProjPosZ; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/unlittwotexture_gl460.vs b/Game.Assets/sdn/shaders/unlittwotexture_gl460.vs index e60b17c3e..60bda0b1b 100644 --- a/Game.Assets/sdn/shaders/unlittwotexture_gl460.vs +++ b/Game.Assets/sdn/shaders/unlittwotexture_gl460.vs @@ -30,9 +30,9 @@ const bool g_bSkinning = SKINNING != 0; #define cBaseTexCoordTransform2_0 vs_const[SHADER_SPECIFIC_CONST_2 + 0] #define cBaseTexCoordTransform2_1 vs_const[SHADER_SPECIFIC_CONST_2 + 1] -out vec2 vs_BaseTexCoord; -out vec2 vs_BaseTexCoord2; -out vec4 vs_WorldPos_ProjPosZ; +layout(location = 0) out vec2 vs_BaseTexCoord; +layout(location = 1) out vec2 vs_BaseTexCoord2; +layout(location = 2) out vec4 vs_WorldPos_ProjPosZ; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.fs b/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.fs index 0b13ca07c..e2b26bd25 100644 --- a/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.fs +++ b/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.fs @@ -18,14 +18,14 @@ // DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" // DYNAMIC: "PIXELFOGTYPE" "0..2" -in vec4 vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY; -in vec3 vs_LightAtten; -in vec4 vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ; -in vec3 vs_WorldNormal; // World-space normal -in vec4 vs_WorldTangent; -in vec4 vs_ProjPos; -in vec4 vs_WorldPos_ProjPosZ; -in vec3 vs_DetailTexCoord_Atten3; +layout(location = 0) in vec4 vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY; +layout(location = 1) in vec3 vs_LightAtten; +layout(location = 2) in vec4 vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ; +layout(location = 3) in vec3 vs_WorldNormal; // World-space normal +layout(location = 4) in vec4 vs_WorldTangent; +layout(location = 5) in vec4 vs_ProjPos; +layout(location = 6) in vec4 vs_WorldPos_ProjPosZ; +layout(location = 7) in vec3 vs_DetailTexCoord_Atten3; layout(std140, binding = 6) uniform source_ps_constants { vec4 ps_const[256]; diff --git a/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.vs b/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.vs index c3a06f430..b88c51fc0 100644 --- a/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.vs +++ b/Game.Assets/sdn/shaders/vertexlitgeneric_bump_gl460.vs @@ -42,17 +42,17 @@ const bool g_bSkinning = SKINNING != 0; //----------------------------------------------------------------------------- // Output vertex format //----------------------------------------------------------------------------- -out vec4 vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY; -out vec3 vs_LightAtten; -out vec4 vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ; -out vec3 vs_WorldNormal; // World-space normal -out vec4 vs_WorldTangent; +layout(location = 0) out vec4 vs_BaseTexCoord2_TangentSpaceVertToEyeVectorXY; +layout(location = 1) out vec3 vs_LightAtten; +layout(location = 2) out vec4 vs_WorldVertToEyeVectorXYZ_TangentSpaceVertToEyeVectorZ; +layout(location = 3) out vec3 vs_WorldNormal; // World-space normal +layout(location = 4) out vec4 vs_WorldTangent; #if USE_WITH_2B -out vec4 vs_ProjPos; +layout(location = 5) out vec4 vs_ProjPos; #else #endif -out vec4 vs_WorldPos_ProjPosZ; -out vec3 vs_DetailTexCoord_Atten3; +layout(location = 6) out vec4 vs_WorldPos_ProjPosZ; +layout(location = 7) out vec3 vs_DetailTexCoord_Atten3; //----------------------------------------------------------------------------- // Main shader entry point diff --git a/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.fs b/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.fs index 74164bb11..ab0b15859 100644 --- a/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.fs +++ b/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.fs @@ -27,28 +27,28 @@ // DYNAMIC: "PIXELFOGTYPE" "0..2" #if SEAMLESS_BASE -in vec3 vs_SeamlessTexCoord; +layout(location = 0) in vec3 vs_SeamlessTexCoord; #define i_baseTexCoord vs_SeamlessTexCoord #else -in vec2 vs_BaseTexCoord; +layout(location = 1) in vec2 vs_BaseTexCoord; #define i_baseTexCoord vs_BaseTexCoord #endif #if SEAMLESS_DETAIL -in vec3 vs_SeamlessDetailTexCoord; +layout(location = 2) in vec3 vs_SeamlessDetailTexCoord; #define i_detailTexCoord vs_SeamlessDetailTexCoord #else -in vec2 vs_DetailTexCoord; +layout(location = 3) in vec2 vs_DetailTexCoord; #define i_detailTexCoord vs_DetailTexCoord #endif -in vec4 vs_Color; +layout(location = 4) in vec4 vs_Color; #if CUBEMAP -in vec3 vs_WorldVertToEyeVector; +layout(location = 5) in vec3 vs_WorldVertToEyeVector; #endif -in vec3 vs_WorldSpaceNormal; -in vec4 vs_ProjPos; -in vec4 vs_WorldPos_ProjPosZ; +layout(location = 6) in vec3 vs_WorldSpaceNormal; +layout(location = 7) in vec4 vs_ProjPos; +layout(location = 8) in vec4 vs_WorldPos_ProjPosZ; #if SEAMLESS_DETAIL || SEAMLESS_BASE -in vec3 vs_SeamlessWeights; +layout(location = 9) in vec3 vs_SeamlessWeights; #endif layout(std140, binding = 6) uniform source_ps_constants { diff --git a/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.vs b/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.vs index 662ebf713..05de97b49 100644 --- a/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.vs +++ b/Game.Assets/sdn/shaders/vertexlitgeneric_gl460.vs @@ -56,27 +56,27 @@ const bool g_bVertexColor = VERTEXCOLOR != 0; const bool g_bHalfLambert = HALFLAMBERT != 0; #if SEAMLESS_BASE -out vec3 vs_SeamlessTexCoord; // Base texture x/y/z (indexed by swizzle) +layout(location = 0) out vec3 vs_SeamlessTexCoord; // Base texture x/y/z (indexed by swizzle) #else -out vec2 vs_BaseTexCoord; // Base texture coordinate +layout(location = 1) out vec2 vs_BaseTexCoord; // Base texture coordinate #endif #if SEAMLESS_DETAIL -out vec3 vs_SeamlessDetailTexCoord; // Detail texture coordinate +layout(location = 2) out vec3 vs_SeamlessDetailTexCoord; // Detail texture coordinate #else -out vec2 vs_DetailTexCoord; // Detail texture coordinate +layout(location = 3) out vec2 vs_DetailTexCoord; // Detail texture coordinate #endif -out vec4 vs_Color; // Vertex color (from lighting or unlit) +layout(location = 4) out vec4 vs_Color; // Vertex color (from lighting or unlit) #if CUBEMAP -out vec3 vs_WorldVertToEyeVector; // Necessary for cubemaps +layout(location = 5) out vec3 vs_WorldVertToEyeVector; // Necessary for cubemaps #endif -out vec3 vs_WorldSpaceNormal; // Necessary for cubemaps and flashlight +layout(location = 6) out vec3 vs_WorldSpaceNormal; // Necessary for cubemaps and flashlight -out vec4 vs_ProjPos; -out vec4 vs_WorldPos_ProjPosZ; +layout(location = 7) out vec4 vs_ProjPos; +layout(location = 8) out vec4 vs_WorldPos_ProjPosZ; #if SEAMLESS_DETAIL || SEAMLESS_BASE -out vec3 vs_SeamlessWeights; // x y z projection weights +layout(location = 9) out vec3 vs_SeamlessWeights; // x y z projection weights #endif #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/water_gl460.fs b/Game.Assets/sdn/shaders/water_gl460.fs index f3524fa54..d4cc29470 100644 --- a/Game.Assets/sdn/shaders/water_gl460.fs +++ b/Game.Assets/sdn/shaders/water_gl460.fs @@ -9,17 +9,17 @@ // DYNAMIC: "PIXELFOGTYPE" "0..1" // DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" -in vec2 vs_BumpTexCoord; -in vec3 vs_TangentEyeVect; -in vec4 vs_ReflectXY_RefractYX; -in float vs_W; -in vec4 vs_ProjPos; +layout(location = 0) in vec2 vs_BumpTexCoord; +layout(location = 1) in vec3 vs_TangentEyeVect; +layout(location = 2) in vec4 vs_ReflectXY_RefractYX; +layout(location = 3) in float vs_W; +layout(location = 4) in vec4 vs_ProjPos; #if MULTITEXTURE -in vec4 vs_ExtraBumpTexCoord; +layout(location = 5) in vec4 vs_ExtraBumpTexCoord; #endif #if BASETEXTURE -centroid in vec4 vs_LightmapTexCoord1And2; -centroid in vec4 vs_LightmapTexCoord3; +layout(location = 6) centroid in vec4 vs_LightmapTexCoord1And2; +layout(location = 7) centroid in vec4 vs_LightmapTexCoord3; #endif layout(std140, binding = 6) uniform source_ps_constants { diff --git a/Game.Assets/sdn/shaders/water_gl460.vs b/Game.Assets/sdn/shaders/water_gl460.vs index 92be927a9..2ee0cb323 100644 --- a/Game.Assets/sdn/shaders/water_gl460.vs +++ b/Game.Assets/sdn/shaders/water_gl460.vs @@ -29,17 +29,17 @@ const int SHADER_SPECIFIC_CONST_3 = 51; #define cBumpTexCoordTransform1 vs_const[SHADER_SPECIFIC_CONST_1 + 1] #define TexOffsets vs_const[SHADER_SPECIFIC_CONST_3] -out vec2 vs_BumpTexCoord; -out vec3 vs_TangentEyeVect; -out vec4 vs_ReflectXY_RefractYX; -out float vs_W; -out vec4 vs_ProjPos; +layout(location = 0) out vec2 vs_BumpTexCoord; +layout(location = 1) out vec3 vs_TangentEyeVect; +layout(location = 2) out vec4 vs_ReflectXY_RefractYX; +layout(location = 3) out float vs_W; +layout(location = 4) out vec4 vs_ProjPos; #if MULTITEXTURE -out vec4 vs_ExtraBumpTexCoord; +layout(location = 5) out vec4 vs_ExtraBumpTexCoord; #endif #if BASETEXTURE -centroid out vec4 vs_LightmapTexCoord1And2; -centroid out vec4 vs_LightmapTexCoord3; +layout(location = 6) centroid out vec4 vs_LightmapTexCoord1And2; +layout(location = 7) centroid out vec4 vs_LightmapTexCoord3; #endif #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/watercheap_gl460.fs b/Game.Assets/sdn/shaders/watercheap_gl460.fs index 78e6aead5..6e1640b9e 100644 --- a/Game.Assets/sdn/shaders/watercheap_gl460.fs +++ b/Game.Assets/sdn/shaders/watercheap_gl460.fs @@ -8,12 +8,12 @@ // DYNAMIC: "HDRENABLED" "0..1" // DYNAMIC: "PIXELFOGTYPE" "0..1" -in vec2 vs_NormalMapTexCoord; -in vec3 vs_WorldVertToEyeVector; -in mat3 vs_TangentSpaceTranspose; -in vec4 vs_Refract_W_ProjZ; +layout(location = 0) in vec2 vs_NormalMapTexCoord; +layout(location = 1) in vec3 vs_WorldVertToEyeVector; +layout(location = 2) in mat3 vs_TangentSpaceTranspose; +layout(location = 5) in vec4 vs_Refract_W_ProjZ; #if MULTITEXTURE -in vec4 vs_ExtraBumpTexCoord; +layout(location = 6) in vec4 vs_ExtraBumpTexCoord; #endif layout(std140, binding = 6) uniform source_ps_constants { diff --git a/Game.Assets/sdn/shaders/watercheap_gl460.vs b/Game.Assets/sdn/shaders/watercheap_gl460.vs index dba0acdf7..12a6c6ac9 100644 --- a/Game.Assets/sdn/shaders/watercheap_gl460.vs +++ b/Game.Assets/sdn/shaders/watercheap_gl460.vs @@ -24,11 +24,11 @@ const int SHADER_SPECIFIC_CONST_3 = 51; #define VSHADER_VECT_SCALE 1.0 #define TexOffsets vs_const[SHADER_SPECIFIC_CONST_3] -out vec2 vs_NormalMapTexCoord; -out vec3 vs_WorldVertToEyeVector; -out mat3 vs_TangentSpaceTranspose; -out vec4 vs_Refract_W_ProjZ; -out vec4 vs_ExtraBumpTexCoord; +layout(location = 0) out vec2 vs_NormalMapTexCoord; +layout(location = 1) out vec3 vs_WorldVertToEyeVector; +layout(location = 2) out mat3 vs_TangentSpaceTranspose; +layout(location = 5) out vec4 vs_Refract_W_ProjZ; +layout(location = 6) out vec4 vs_ExtraBumpTexCoord; #include "common_clipplanes_gl460.vs" diff --git a/Game.Assets/sdn/shaders/worldvertextransition_gl460.fs b/Game.Assets/sdn/shaders/worldvertextransition_gl460.fs index d0b72d861..20a0326b0 100644 --- a/Game.Assets/sdn/shaders/worldvertextransition_gl460.fs +++ b/Game.Assets/sdn/shaders/worldvertextransition_gl460.fs @@ -1,7 +1,7 @@ #version 460 -in vec2 vs_TexCoord0; -centroid in vec2 vs_TexCoord1; -in vec4 vs_Color; +layout(location = 0) in vec2 vs_TexCoord0; +layout(location = 1) centroid in vec2 vs_TexCoord1; +layout(location = 2) in vec4 vs_Color; layout(std140, binding = 3) uniform source_pixel_sharedUBO { bool isAlphaTesting; diff --git a/Game.Assets/sdn/shaders/worldvertextransition_gl460.vs b/Game.Assets/sdn/shaders/worldvertextransition_gl460.vs index 250e1827a..d009c5ca2 100644 --- a/Game.Assets/sdn/shaders/worldvertextransition_gl460.vs +++ b/Game.Assets/sdn/shaders/worldvertextransition_gl460.vs @@ -11,9 +11,9 @@ layout(std140, binding = 0) uniform source_matrices { mat4 modelMatrix; }; -out vec2 vs_TexCoord0; -centroid out vec2 vs_TexCoord1; -out vec4 vs_Color; +layout(location = 0) out vec2 vs_TexCoord0; +layout(location = 1) centroid out vec2 vs_TexCoord1; +layout(location = 2) out vec4 vs_Color; #include "common_clipplanes_gl460.vs" From e1ff92c131b2d6110a14463db297f15fbb7c0b0e Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:45:24 -0700 Subject: [PATCH 14/17] Different attempt for shared context I believe the previous approach would break on Wayland (have had bad juju from previous multithreaded game engine experiments) --- Source.SDLManager/SDL3_LauncherManager.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Source.SDLManager/SDL3_LauncherManager.cs b/Source.SDLManager/SDL3_LauncherManager.cs index 61f316a50..95b9c59ce 100644 --- a/Source.SDLManager/SDL3_LauncherManager.cs +++ b/Source.SDLManager/SDL3_LauncherManager.cs @@ -72,7 +72,8 @@ public unsafe class SDL3_OpenGL46_Context(nint window, nint ctx) : IGraphicsCont public nint HardwareHandle => ctx; public void MakeCurrent() { - SDL3.SDL_GL_MakeCurrent((SDL_Window*)window, (SDL_GLContextState*)ctx); + if (!SDL3.SDL_GL_MakeCurrent((SDL_Window*)window, (SDL_GLContextState*)ctx)) + Warning($"Could not make GL context current: {SDL3.SDL_GetError()}\n"); } public void SetSwapInterval(float swapInterval) { @@ -87,18 +88,26 @@ public void SwapBuffers() { } public IGraphicsContext? CreateSharedContext() { + SDL_Window* sharedWindow = SDL3.SDL_CreateWindow("", 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN); + if (sharedWindow == null) { + Warning($"Could not create shared GL context window: {SDL3.SDL_GetError()}\n"); + return null; + } + MakeCurrent(); SDL3.SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - nint shared = (nint)SDL3.SDL_GL_CreateContext((SDL_Window*)window); + nint shared = (nint)SDL3.SDL_GL_CreateContext(sharedWindow); SDL3.SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0); - MakeCurrent(); if (shared == 0) { Warning($"Could not create shared GL context: {SDL3.SDL_GetError()}\n"); + MakeCurrent(); + SDL3.SDL_DestroyWindow(sharedWindow); return null; } - return new SDL3_OpenGL46_Context(window, shared); + MakeCurrent(); + return new SDL3_OpenGL46_Context((nint)sharedWindow, shared); } } From 9a4f5d5437f34e1b2bc41cbfc53ddfc22805d3c7 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:47:25 -0700 Subject: [PATCH 15/17] Keep per-stage pixel and vertex shaders --- Source.ShaderAPI.Gl46/ShaderAPIGl46.cs | 77 +++++++++++++++++--------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs index c0e73d6f2..95c726cb5 100644 --- a/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs +++ b/Source.ShaderAPI.Gl46/ShaderAPIGl46.cs @@ -1805,17 +1805,29 @@ public unsafe int LocateShaderUniform(ReadOnlySpan name) { return -1; ShaderSystem shaders = (ShaderSystem)ShaderManager; - int loc = LocateStageUniform(shaders.GetShaderProgram(activePixelShader), name); - if (loc != -1) - return loc; + uint pixelProgram = shaders.GetShaderProgram(activePixelShader); + uint vertexProgram = shaders.GetShaderProgram(activeVertexShader); + StageUniform stage = new(pixelProgram, LocateStageUniform(pixelProgram, name), vertexProgram, LocateStageUniform(vertexProgram, name)); + if (stage.PixelLocation == -1 && stage.VertexLocation == -1) + return -1; + + if (!stageUniformHandles.TryGetValue(stage, out int handle)) { + handle = stageUniforms.Count; + stageUniforms.Add(stage); + stageUniformHandles[stage] = handle; + } - loc = LocateStageUniform(shaders.GetShaderProgram(activeVertexShader), name); - return loc == -1 ? -1 : loc | VertexStageUniform; + return handle; } - const int VertexStageUniform = 0x40000000; + readonly record struct StageUniform(uint PixelProgram, int PixelLocation, uint VertexProgram, int VertexLocation); + readonly List stageUniforms = []; + readonly Dictionary stageUniformHandles = []; private unsafe int LocateStageUniform(uint shader, ReadOnlySpan name) { + if (shader == 0) + return -1; + // Then get shader ID -> shader uniform lookup table if (!locs.TryGetValue(shader, out var lookup)) lookup = locs[shader] = []; @@ -1835,23 +1847,20 @@ private unsafe int LocateStageUniform(uint shader, ReadOnlySpan name) { } public nint GetCurrentProgram() => (nint)CombobulateShadersIfChanged(); - uint GetCurrentProgramInternal() => CombobulateShadersIfChanged(); readonly Dictionary> uniformValues = []; - bool UniformChanged(ref int uniform, ReadOnlySpan values, out uint program) { - program = 0; - if (uniform < 0 || GetCurrentProgramInternal() == 0) + bool GetStageUniform(int uniform, out StageUniform stage) { + if ((uint)uniform >= (uint)stageUniforms.Count) { + stage = default; return false; - - ShaderSystem shaders = (ShaderSystem)ShaderManager; - if ((uniform & VertexStageUniform) != 0) { - uniform &= ~VertexStageUniform; - program = shaders.GetShaderProgram(activeVertexShader); } - else - program = shaders.GetShaderProgram(activePixelShader); - if (program == 0) + stage = stageUniforms[uniform]; + return true; + } + + bool UniformChanged(uint program, int uniform, ReadOnlySpan values) { + if (program == 0 || uniform < 0) return false; if (!uniformValues.TryGetValue(program, out var cache)) @@ -1868,12 +1877,17 @@ bool UniformChanged(ref int uniform, ReadOnlySpan values, out uint progra } public void SetShaderUniform(int uniform, int integer) { - if (!UniformChanged(ref uniform, [BitConverter.Int32BitsToSingle(integer)], out uint program)) + if (!GetStageUniform(uniform, out StageUniform stage)) return; + + ReadOnlySpan values = [BitConverter.Int32BitsToSingle(integer)]; #if GL_DEBUG int i = glGetError(); #endif - glProgramUniform1i(program, uniform, integer); + if (UniformChanged(stage.PixelProgram, stage.PixelLocation, values)) + glProgramUniform1i(stage.PixelProgram, stage.PixelLocation, integer); + if (UniformChanged(stage.VertexProgram, stage.VertexLocation, values)) + glProgramUniform1i(stage.VertexProgram, stage.VertexLocation, integer); #if GL_DEBUG if ((i = glGetError()) != 0) AssertMsg(false, $"GL error {i}"); @@ -1881,24 +1895,35 @@ public void SetShaderUniform(int uniform, int integer) { } public void SetShaderUniform(int uniform, uint integer) { - if (!UniformChanged(ref uniform, [BitConverter.UInt32BitsToSingle(integer)], out uint program)) + if (!GetStageUniform(uniform, out StageUniform stage)) return; - glProgramUniform1ui(program, uniform, integer); + ReadOnlySpan values = [BitConverter.UInt32BitsToSingle(integer)]; + if (UniformChanged(stage.PixelProgram, stage.PixelLocation, values)) + glProgramUniform1ui(stage.PixelProgram, stage.PixelLocation, integer); + if (UniformChanged(stage.VertexProgram, stage.VertexLocation, values)) + glProgramUniform1ui(stage.VertexProgram, stage.VertexLocation, integer); } public void SetShaderUniform(int uniform, float fl) { - if (!UniformChanged(ref uniform, [fl], out uint program)) + if (!GetStageUniform(uniform, out StageUniform stage)) return; - glProgramUniform1f(program, uniform, fl); + ReadOnlySpan values = [fl]; + if (UniformChanged(stage.PixelProgram, stage.PixelLocation, values)) + glProgramUniform1f(stage.PixelProgram, stage.PixelLocation, fl); + if (UniformChanged(stage.VertexProgram, stage.VertexLocation, values)) + glProgramUniform1f(stage.VertexProgram, stage.VertexLocation, fl); } public void SetShaderUniform(int uniform, ReadOnlySpan flConsts) { - if (!UniformChanged(ref uniform, flConsts, out uint program)) + if (!GetStageUniform(uniform, out StageUniform stage)) return; - glProgramUniform1fv(program, uniform, flConsts); + if (UniformChanged(stage.PixelProgram, stage.PixelLocation, flConsts)) + glProgramUniform1fv(stage.PixelProgram, stage.PixelLocation, flConsts); + if (UniformChanged(stage.VertexProgram, stage.VertexLocation, flConsts)) + glProgramUniform1fv(stage.VertexProgram, stage.VertexLocation, flConsts); } const int GL_TEXTURE_SRGB_DECODE_EXT = 0x8A48; From 1d0751f76595515252cbd94970328f6bf4589cd3 Mon Sep 17 00:00:00 2001 From: march <106459595+marchc1@users.noreply.github.com> Date: Tue, 13 Oct 2026 14:52:30 -0700 Subject: [PATCH 16/17] better shader warnings when this fails --- Source.ShaderAPI.Gl46/ShaderSystem.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index 26577ed31..83d25b1e8 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -706,7 +706,13 @@ private static void ApplyCompileJob(ShaderCompileJob job) { Warning($" [source string {i}] = {job.SourceFiles[i]}\n"); Warning("\n"); } - else if (job.Handle != 0 && !job.FromCache) { + else if (job.Handle == 0) { + if (job.SourceFiles == null) + Warning($"WARNING: {shader.TypeName} shader source not found: {shader.Name}.\n"); + else + Warning($"WARNING: {shader.TypeName} shader program could not be created for {shader.Name}.\n"); + } + else if (!job.FromCache) { ReadOnlySpan combos = shader.Defines.AsSpan().SliceNullTerminatedString(); if (combos.IsEmpty) Msg($"Compiled shader: {shader.Name} ({shader.TypeName})\n"); From f78eb76e91674247431c58f6e5e162bb1c34fd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Wed, 14 Oct 2026 02:21:47 +0300 Subject: [PATCH 17/17] Compile shaders on the main context with parallel shader compile --- Dependencies/OpenGL/Gl46.cs | 2 + Source.Common/Launcher/IGraphicsContext.cs | 1 - Source.SDLManager/SDL3_LauncherManager.cs | 23 ---- Source.ShaderAPI.Gl46/ShaderSystem.cs | 134 ++++++++------------- 4 files changed, 50 insertions(+), 110 deletions(-) diff --git a/Dependencies/OpenGL/Gl46.cs b/Dependencies/OpenGL/Gl46.cs index cb793241e..04ac264b3 100644 --- a/Dependencies/OpenGL/Gl46.cs +++ b/Dependencies/OpenGL/Gl46.cs @@ -14444,6 +14444,8 @@ public static void glSpecializeShader(GLuint shader, string pEntryPoint, GLuint #endif + public const GLenum GL_COMPLETION_STATUS_KHR = 0x91B1; + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void PFNGLMAXSHADERCOMPILERTHREADSKHRPROC(GLuint count); private static PFNGLMAXSHADERCOMPILERTHREADSKHRPROC _glMaxShaderCompilerThreadsKHR; diff --git a/Source.Common/Launcher/IGraphicsContext.cs b/Source.Common/Launcher/IGraphicsContext.cs index 3d40737f4..03c7a15e3 100644 --- a/Source.Common/Launcher/IGraphicsContext.cs +++ b/Source.Common/Launcher/IGraphicsContext.cs @@ -6,5 +6,4 @@ public interface IGraphicsContext { void MakeCurrent(); void SetSwapInterval(float swapInterval); void SwapBuffers(); - IGraphicsContext? CreateSharedContext(); } diff --git a/Source.SDLManager/SDL3_LauncherManager.cs b/Source.SDLManager/SDL3_LauncherManager.cs index 95b9c59ce..01653669b 100644 --- a/Source.SDLManager/SDL3_LauncherManager.cs +++ b/Source.SDLManager/SDL3_LauncherManager.cs @@ -86,29 +86,6 @@ public void SetSwapInterval(float swapInterval) { public void SwapBuffers() { SDL3.SDL_GL_SwapWindow((SDL_Window*)window); } - - public IGraphicsContext? CreateSharedContext() { - SDL_Window* sharedWindow = SDL3.SDL_CreateWindow("", 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN); - if (sharedWindow == null) { - Warning($"Could not create shared GL context window: {SDL3.SDL_GetError()}\n"); - return null; - } - - MakeCurrent(); - SDL3.SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - nint shared = (nint)SDL3.SDL_GL_CreateContext(sharedWindow); - SDL3.SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0); - - if (shared == 0) { - Warning($"Could not create shared GL context: {SDL3.SDL_GetError()}\n"); - MakeCurrent(); - SDL3.SDL_DestroyWindow(sharedWindow); - return null; - } - - MakeCurrent(); - return new SDL3_OpenGL46_Context((nint)sharedWindow, shared); - } } public unsafe class SDL3_LauncherManager : ILauncherManager, IGraphicsProvider diff --git a/Source.ShaderAPI.Gl46/ShaderSystem.cs b/Source.ShaderAPI.Gl46/ShaderSystem.cs index 83d25b1e8..6e2239b3d 100644 --- a/Source.ShaderAPI.Gl46/ShaderSystem.cs +++ b/Source.ShaderAPI.Gl46/ShaderSystem.cs @@ -1,14 +1,12 @@ using Microsoft.Extensions.DependencyInjection; using Source.Common.Filesystem; -using Source.Common.Launcher; using Source.Common.MaterialSystem; using Source.Common.ShaderAPI; using Source.Common.ShaderLib; using Source.MaterialSystem; using System.Buffers.Binary; -using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; @@ -60,8 +58,6 @@ internal sealed class ShaderSource(byte[] text, int versionLength, List public readonly ulong Hash = text.AsSpan().Hash(); } -internal sealed record ShaderCompileBatch(ShaderCompileJob[] Jobs, nint Fence); - public class ShaderSystem : IShaderSystemInternal { List ShaderDLLs = []; @@ -721,7 +717,9 @@ private static void ApplyCompileJob(ShaderCompileJob job) { } } - const int MaxCompileJobsPerBatch = 32; + const int MaxParallelSubmitsPerFrame = 32; + const int MaxBlockingSubmitsPerFrame = 4; + const int MaxSubmitsPerWait = 64; readonly List ShaderObjects = []; readonly Dictionary ShaderPrograms = []; @@ -731,11 +729,8 @@ private static void ApplyCompileJob(ShaderCompileJob job) { string? ProgramCacheDir; readonly Queue PendingCompileJobs = []; readonly Queue HighPriorityCompileJobs = []; - readonly ConcurrentQueue FinishedCompileBatches = []; - readonly List FencedCompileBatches = []; - IGraphicsContext? CompileContext; - Thread? CompileThread; - bool CompileWorkerStarted; + readonly List InFlightCompileJobs = []; + bool CompileInitialized; int OutstandingCompileJobs; bool ParallelShaderCompile; @@ -751,8 +746,8 @@ private static unsafe bool HasGLExtension(ReadOnlySpan extension) { private static unsafe string GetGLString(int name) => Encoding.ASCII.GetString(MemoryMarshal.CreateReadOnlySpanFromNullTerminated(glGetString(name))); - private unsafe void StartCompileWorker() { - CompileWorkerStarted = true; + private unsafe void InitShaderCompile() { + CompileInitialized = true; int binaryFormats = 0; glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &binaryFormats); @@ -764,73 +759,31 @@ private unsafe void StartCompileWorker() { else Warning("Program binaries unsupported, shader cache disabled\n"); - CompileContext = ShaderAPI.Device?.CreateSharedContext(); - if (CompileContext == null) { - Warning("Shader compile worker unavailable, compiling shaders on the main thread\n"); - return; - } - ParallelShaderCompile = glMaxShaderCompilerThreadsKHRLoaded && (HasGLExtension("GL_KHR_parallel_shader_compile"u8) || HasGLExtension("GL_ARB_parallel_shader_compile"u8)); - CompileThread = new Thread(CompileWorker) { Name = "ShaderCompile", IsBackground = true }; - CompileThread.Start(); - } - - private unsafe void CompileWorker() { - CompileContext!.MakeCurrent(); if (ParallelShaderCompile) glMaxShaderCompilerThreadsKHR(2); - - List jobs = []; - while (true) { - lock (PendingCompileJobs) { - while (HighPriorityCompileJobs.Count == 0 && PendingCompileJobs.Count == 0) - Monitor.Wait(PendingCompileJobs); - TakeCompileJobs(HighPriorityCompileJobs, jobs); - TakeCompileJobs(PendingCompileJobs, jobs); - } - - if (jobs.Count == 0) - continue; - - foreach (ShaderCompileJob job in jobs) - SubmitCompileJob(job); - foreach (ShaderCompileJob job in jobs) - FinishCompileJob(job); - - ShaderCompileBatch batch = new([.. jobs], (nint)glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)); - glFlush(); - FinishedCompileBatches.Enqueue(batch); - jobs.Clear(); - } + else + Warning("Parallel shader compile unsupported, shaders compile blocking\n"); } - private static void TakeCompileJobs(Queue queue, List jobs) { - while (queue.Count > 0 && jobs.Count < MaxCompileJobsPerBatch) { - ShaderCompileJob job = queue.Dequeue(); + private ShaderCompileJob? TakeCompileJob() { + while (HighPriorityCompileJobs.Count > 0 || PendingCompileJobs.Count > 0) { + ShaderCompileJob job = HighPriorityCompileJobs.Count > 0 ? HighPriorityCompileJobs.Dequeue() : PendingCompileJobs.Dequeue(); if (job.Taken) continue; job.Taken = true; - jobs.Add(job); + return job; } + return null; } private void QueueCompileJob(ShaderCompileJob job, bool highPriority) { - if (!CompileWorkerStarted) - StartCompileWorker(); - - if (CompileContext == null) { - SubmitCompileJob(job); - FinishCompileJob(job); - ApplyCompileJob(job); - return; - } + if (!CompileInitialized) + InitShaderCompile(); ++OutstandingCompileJobs; - lock (PendingCompileJobs) { - job.HighPriority = highPriority; - (highPriority ? HighPriorityCompileJobs : PendingCompileJobs).Enqueue(job); - Monitor.Pulse(PendingCompileJobs); - } + job.HighPriority = highPriority; + (highPriority ? HighPriorityCompileJobs : PendingCompileJobs).Enqueue(job); } internal void PrioritizeShader(nint handle) { @@ -839,44 +792,53 @@ internal void PrioritizeShader(nint handle) { ShaderObject shader = ShaderObjects[(int)handle]; ShaderCompileJob? job = shader.Job; - if (shader.State != ShaderCompileState.Pending || job == null || job.HighPriority) + if (shader.State != ShaderCompileState.Pending || job == null || job.HighPriority || job.Taken) return; - lock (PendingCompileJobs) { - if (job.Taken) - return; - job.HighPriority = true; - HighPriorityCompileJobs.Enqueue(job); - Monitor.Pulse(PendingCompileJobs); - } + job.HighPriority = true; + HighPriorityCompileJobs.Enqueue(job); } - public void BeginFrame() => ProcessFinishedCompileJobs(); + public void BeginFrame() => ProcessCompileJobs(ParallelShaderCompile ? MaxParallelSubmitsPerFrame : MaxBlockingSubmitsPerFrame); public void WaitForPendingCompiles(Action? progress) { int total = OutstandingCompileJobs; while (OutstandingCompileJobs > 0) { - ProcessFinishedCompileJobs(); + ProcessCompileJobs(MaxSubmitsPerWait); progress?.Invoke(total - OutstandingCompileJobs, total); if (OutstandingCompileJobs > 0) Thread.Sleep(1); } } - private unsafe void ProcessFinishedCompileJobs() { - while (FinishedCompileBatches.TryDequeue(out ShaderCompileBatch? batch)) - FencedCompileBatches.Add(batch); + private void CompleteCompileJob(ShaderCompileJob job) { + FinishCompileJob(job); + ApplyCompileJob(job); + --OutstandingCompileJobs; + } + + private unsafe void ProcessCompileJobs(int maxSubmits) { + for (int i = 0; i < maxSubmits; i++) { + ShaderCompileJob? job = TakeCompileJob(); + if (job == null) + break; + + SubmitCompileJob(job); + if (ParallelShaderCompile && job.Handle != 0) + InFlightCompileJobs.Add(job); + else + CompleteCompileJob(job); + } - for (int i = 0; i < FencedCompileBatches.Count; i++) { - ShaderCompileBatch batch = FencedCompileBatches[i]; - if (glClientWaitSync((void*)batch.Fence, 0, 0) == GL_TIMEOUT_EXPIRED) + for (int i = 0; i < InFlightCompileJobs.Count; i++) { + ShaderCompileJob job = InFlightCompileJobs[i]; + int complete = GL_TRUE; + glGetProgramiv(job.Handle, GL_COMPLETION_STATUS_KHR, &complete); + if (complete != GL_TRUE) continue; - glDeleteSync((void*)batch.Fence); - foreach (ShaderCompileJob job in batch.Jobs) - ApplyCompileJob(job); - OutstandingCompileJobs -= batch.Jobs.Length; - FencedCompileBatches.RemoveAt(i--); + CompleteCompileJob(job); + InFlightCompileJobs.RemoveAt(i--); } }