From f333e64812ffe9e5ce44de58e230275ef34534e2 Mon Sep 17 00:00:00 2001 From: dexy Date: Wed, 4 Dec 2019 20:14:11 +1100 Subject: [PATCH] Hardware MSAA support for deferred render --- CodeWalker.Shaders/CodeWalker.Shaders.vcxproj | 20 ++++ .../CodeWalker.Shaders.vcxproj.filters | 6 ++ CodeWalker.Shaders/DirLightPS_MS.hlsl | 66 +++++++++++++ CodeWalker.Shaders/LightPS.hlsli | 4 +- CodeWalker.Shaders/LodLightsPS_MS.hlsl | 53 +++++++++++ Rendering/DirectX/DXManager.cs | 2 +- Rendering/DirectX/DXUtility.cs | 4 + Rendering/Shaders/DeferredScene.cs | 87 +++++++++++------- Rendering/Utils/GpuBuffers.cs | 37 +++++--- Shaders/DirLightPS.cso | Bin 7464 -> 7476 bytes Shaders/DirLightPS_MS.cso | Bin 0 -> 40856 bytes Shaders/LightPS.cso | Bin 10680 -> 10696 bytes Shaders/LodLightsPS.cso | Bin 5232 -> 5264 bytes Shaders/LodLightsPS_MS.cso | Bin 0 -> 28272 bytes 14 files changed, 231 insertions(+), 48 deletions(-) create mode 100644 CodeWalker.Shaders/DirLightPS_MS.hlsl create mode 100644 CodeWalker.Shaders/LodLightsPS_MS.hlsl create mode 100644 Shaders/DirLightPS_MS.cso create mode 100644 Shaders/LodLightsPS_MS.cso diff --git a/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj b/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj index 5dc55ff..2af5424 100644 --- a/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj +++ b/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj @@ -392,6 +392,16 @@ Pixel 4.0 + + Pixel + 4.1 + Pixel + 4.1 + Pixel + 4.1 + Pixel + 4.1 + Vertex 4.0 @@ -434,6 +444,16 @@ Vertex 4.0 + + Pixel + 4.1 + Pixel + 4.1 + Pixel + 4.1 + Pixel + 4.1 + Pixel 4.0 diff --git a/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj.filters b/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj.filters index 424bf17..b6bf4d5 100644 --- a/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj.filters +++ b/CodeWalker.Shaders/CodeWalker.Shaders.vcxproj.filters @@ -262,6 +262,12 @@ Lights + + Lights + + + Lights + diff --git a/CodeWalker.Shaders/DirLightPS_MS.hlsl b/CodeWalker.Shaders/DirLightPS_MS.hlsl new file mode 100644 index 0000000..1beb390 --- /dev/null +++ b/CodeWalker.Shaders/DirLightPS_MS.hlsl @@ -0,0 +1,66 @@ +#include "LightPS.hlsli" + + +Texture2DMS DepthTex : register(t0); +Texture2DMS DiffuseTex : register(t2); +Texture2DMS NormalTex : register(t3); +Texture2DMS SpecularTex : register(t4); +Texture2DMS IrradianceTex : register(t5); + +struct VS_Output +{ + float4 Pos : SV_POSITION; + float4 Screen : TEXCOORD0; +}; + +PS_OUTPUT main(VS_Output input) +{ + + //switch (RenderMode) + //{ + // case 5: c += diffuse.rgb; break; + // case 6: c += normal.rgb; break; + // case 7: c += specular.rgb; break; + //} + + uint2 ssloc = uint2(input.Pos.xy); //pixel location + float2 spos = float2(input.Screen.xy / input.Screen.w); + float3 c = 0; + float d = 0; + float a = 0; + int sc = min(SampleCount, 8); + + [unroll] + for (int i = 0; i < sc; i++) + { + float depth = DepthTex.Load(ssloc, i); + if (depth == 0) continue; //no existing subpixel rendered here + + float4 diffuse = DiffuseTex.Load(ssloc, i); + float4 normal = NormalTex.Load(ssloc, i); + float4 specular = SpecularTex.Load(ssloc, i); + float4 irradiance = IrradianceTex.Load(ssloc, i); + + float4 cpos = mul(float4(spos, depth, 1), ViewProjInv); + float3 camRel = cpos.xyz * (1 / cpos.w); + float3 norm = normal.xyz * 2 - 1; + + float3 colour = DeferredDirectionalLight(camRel, norm, diffuse, specular, irradiance); + + c += colour; + d += depth; + a += 1; + } + + c *= SampleMult; + d *= SampleMult; + a *= SampleMult; + + if (d <= 0) discard; + + PS_OUTPUT output; + output.Colour = float4(c, a); + output.Depth = d; + return output; +} + diff --git a/CodeWalker.Shaders/LightPS.hlsli b/CodeWalker.Shaders/LightPS.hlsli index 1336438..7bb7002 100644 --- a/CodeWalker.Shaders/LightPS.hlsli +++ b/CodeWalker.Shaders/LightPS.hlsli @@ -18,8 +18,8 @@ cbuffer PSLightVars : register(b0) uint RenderSamplerCoord; uint LightType; //0=directional, 1=Point, 2=Spot, 4=Capsule uint IsLOD; //useful or not? - uint Pad0; - uint Pad1; + uint SampleCount;//for MSAA + float SampleMult;//for MSAA } diff --git a/CodeWalker.Shaders/LodLightsPS_MS.hlsl b/CodeWalker.Shaders/LodLightsPS_MS.hlsl new file mode 100644 index 0000000..cb7d0f8 --- /dev/null +++ b/CodeWalker.Shaders/LodLightsPS_MS.hlsl @@ -0,0 +1,53 @@ +#include "LightPS.hlsli" + + +Texture2DMS DepthTex : register(t0); +Texture2DMS DiffuseTex : register(t2); +Texture2DMS NormalTex : register(t3); +Texture2DMS SpecularTex : register(t4); +Texture2DMS IrradianceTex : register(t5); + +struct VS_Output +{ + float4 Pos : SV_POSITION; + float4 Screen : TEXCOORD0; + uint IID : SV_INSTANCEID; +}; + +float4 main(VS_Output input) : SV_TARGET +{ + uint2 ssloc = uint2(input.Pos.xy); //pixel location + float2 spos = float2(input.Screen.xy / input.Screen.w); + float4 c = 0; + float d = 0; + int sc = min(SampleCount, 8); + + [unroll] + for (int i = 0; i < sc; i++) + { + float depth = DepthTex.Load(ssloc, i); + if (depth == 0) continue; //no existing subpixel rendered here + + float4 diffuse = DiffuseTex.Load(ssloc, i); + float4 normal = NormalTex.Load(ssloc, i); + float4 specular = SpecularTex.Load(ssloc, i); + float4 irradiance = IrradianceTex.Load(ssloc, i); + + float4 cpos = mul(float4(spos, depth, 1), ViewProjInv); + float3 camRel = cpos.xyz * (1 / cpos.w); + float3 norm = normal.xyz * 2 - 1; + + float4 colour = DeferredLODLight(camRel, norm, diffuse, specular, irradiance, input.IID); + + c += colour; + d += depth; + } + + c *= SampleMult; + d *= SampleMult; + + if (d <= 0) discard; + + return c; +} + diff --git a/Rendering/DirectX/DXManager.cs b/Rendering/DirectX/DXManager.cs index bc099af..b3ca953 100644 --- a/Rendering/DirectX/DXManager.cs +++ b/Rendering/DirectX/DXManager.cs @@ -63,7 +63,7 @@ namespace CodeWalker.Rendering Usage = Usage.RenderTargetOutput }; - FeatureLevel[] levels = new FeatureLevel[] { FeatureLevel.Level_10_0 }; + FeatureLevel[] levels = new FeatureLevel[] { FeatureLevel.Level_11_0, FeatureLevel.Level_10_1, FeatureLevel.Level_10_0 }; DeviceCreationFlags flags = DeviceCreationFlags.None; //#if DEBUG diff --git a/Rendering/DirectX/DXUtility.cs b/Rendering/DirectX/DXUtility.cs index 0b4dec7..7480147 100644 --- a/Rendering/DirectX/DXUtility.cs +++ b/Rendering/DirectX/DXUtility.cs @@ -132,6 +132,10 @@ namespace CodeWalker.Rendering srvd.Texture3D.MipLevels = mipLevels; srvd.Texture3D.MostDetailedMip = mostDetailedMip; break; + case ShaderResourceViewDimension.Texture2DMultisampled: + case ShaderResourceViewDimension.Texture2DMultisampledArray: + //nothing to do here + break; default: throw new Exception(); //not implemented.... } diff --git a/Rendering/Shaders/DeferredScene.cs b/Rendering/Shaders/DeferredScene.cs index 48574ba..92b8b3e 100644 --- a/Rendering/Shaders/DeferredScene.cs +++ b/Rendering/Shaders/DeferredScene.cs @@ -37,8 +37,8 @@ namespace CodeWalker.Rendering public uint RenderSamplerCoord; //which texcoord to use in single texture mode public uint LightType; //0=directional, 1=Point, 2=Spot, 4=Capsule public uint IsLOD; //useful or not? - public uint Pad0; - public uint Pad1; + public uint SampleCount;//for MSAA + public float SampleMult;//for MSAA } public struct DeferredSSAAPSVars @@ -66,8 +66,10 @@ namespace CodeWalker.Rendering VertexShader DirLightVS; PixelShader DirLightPS; + PixelShader DirLightMSPS; VertexShader LodLightVS; PixelShader LodLightPS; + PixelShader LodLightMSPS; UnitCone LightCone; UnitSphere LightSphere; UnitCapsule LightCapsule; @@ -86,6 +88,11 @@ namespace CodeWalker.Rendering public int SSAASampleCount = 1; + public int MSAASampleCount = 4; + + + + public long VramUsage { get @@ -102,8 +109,10 @@ namespace CodeWalker.Rendering byte[] bDirLightVS = File.ReadAllBytes("Shaders\\DirLightVS.cso"); byte[] bDirLightPS = File.ReadAllBytes("Shaders\\DirLightPS.cso"); + byte[] bDirLightMSPS = File.ReadAllBytes("Shaders\\DirLightPS_MS.cso"); byte[] bLodLightVS = File.ReadAllBytes("Shaders\\LodLightsVS.cso"); byte[] bLodLightPS = File.ReadAllBytes("Shaders\\LodLightsPS.cso"); + byte[] bLodLightMSPS = File.ReadAllBytes("Shaders\\LodLightsPS_MS.cso"); byte[] bFinalVS = File.ReadAllBytes("Shaders\\PPFinalPassVS.cso"); byte[] bSSAAPS = File.ReadAllBytes("Shaders\\PPSSAAPS.cso"); @@ -111,6 +120,19 @@ namespace CodeWalker.Rendering DirLightPS = new PixelShader(device, bDirLightPS); LodLightVS = new VertexShader(device, bLodLightVS); LodLightPS = new PixelShader(device, bLodLightPS); + + try + { + //error could happen here if the device isn't supporting feature level 10.1 + DirLightMSPS = new PixelShader(device, bDirLightMSPS); + LodLightMSPS = new PixelShader(device, bLodLightMSPS); + } + catch + { + MSAASampleCount = 1; //can't do MSAA without at least 10.1 support + } + + LightCone = new UnitCone(device, bLodLightVS, 4, false); LightSphere = new UnitSphere(device, bLodLightVS, 4, true); LightCapsule = new UnitCapsule(device, bLodLightVS, 4, false); @@ -198,6 +220,11 @@ namespace CodeWalker.Rendering DirLightPS.Dispose(); DirLightPS = null; } + if (DirLightMSPS != null) + { + DirLightMSPS.Dispose(); + DirLightMSPS = null; + } if (DirLightVS != null) { DirLightVS.Dispose(); @@ -208,6 +235,11 @@ namespace CodeWalker.Rendering LodLightPS.Dispose(); LodLightPS = null; } + if (LodLightMSPS != null) + { + LodLightMSPS.Dispose(); + LodLightMSPS = null; + } if (LodLightVS != null) { LodLightVS.Dispose(); @@ -249,7 +281,7 @@ namespace CodeWalker.Rendering Viewport.Y = 0.0f; - GBuffers = new GpuMultiTexture(device, uw, uh, 4, Format.R8G8B8A8_UNorm, true, Format.D32_Float); + GBuffers = new GpuMultiTexture(device, uw, uh, 4, Format.R8G8B8A8_UNorm, true, Format.D32_Float, MSAASampleCount); WindowSizeVramUsage += GBuffers.VramUsage; SceneColour = new GpuTexture(device, uw, uh, Format.R32G32B32A32_Float, 1, 0, true, Format.D32_Float); @@ -293,18 +325,18 @@ namespace CodeWalker.Rendering public void RenderLights(DeviceContext context, Camera camera, Shadowmap globalShadows, ShaderGlobalLights globalLights) { - uint rendermode = 0; - uint rendermodeind = 1; //first full-screen directional light pass, for sun/moon //discard pixels where scene depth is 0, since nothing was rendered there //blend mode: overwrite - context.VertexShader.Set(DirLightVS); - context.PixelShader.Set(DirLightPS); + var ps = (MSAASampleCount > 1) ? DirLightMSPS : DirLightPS; - LightVSVars.Vars.ViewProj = Matrix.Identity; //Matrix.Transpose(camera.ViewProjMatrix); - LightVSVars.Vars.CameraPos = Vector4.Zero; //new Vector4(camera.Position, 0.0f); + context.VertexShader.Set(DirLightVS); + context.PixelShader.Set(ps); + + LightVSVars.Vars.ViewProj = Matrix.Identity; + LightVSVars.Vars.CameraPos = Vector4.Zero; LightVSVars.Vars.LightType = 0; LightVSVars.Vars.IsLOD = 0; LightVSVars.Vars.Pad0 = 0; @@ -314,15 +346,15 @@ namespace CodeWalker.Rendering LightPSVars.Vars.GlobalLights = globalLights.Params; LightPSVars.Vars.ViewProjInv = Matrix.Transpose(camera.ViewProjInvMatrix); - LightPSVars.Vars.CameraPos = Vector4.Zero; //new Vector4(camera.Position, 0.0f); + LightPSVars.Vars.CameraPos = Vector4.Zero; LightPSVars.Vars.EnableShadows = (globalShadows != null) ? 1u : 0u; - LightPSVars.Vars.RenderMode = rendermode; - LightPSVars.Vars.RenderModeIndex = rendermodeind; - LightPSVars.Vars.RenderSamplerCoord = 0;// (uint)RenderTextureSamplerCoord; + LightPSVars.Vars.RenderMode = 0; + LightPSVars.Vars.RenderModeIndex = 1; + LightPSVars.Vars.RenderSamplerCoord = 0; LightPSVars.Vars.LightType = 0; LightPSVars.Vars.IsLOD = 0; - LightPSVars.Vars.Pad0 = 0; - LightPSVars.Vars.Pad1 = 0; + LightPSVars.Vars.SampleCount = (uint)MSAASampleCount; + LightPSVars.Vars.SampleMult = 1.0f / MSAASampleCount; LightPSVars.Update(context); LightPSVars.SetPSCBuffer(context, 0); @@ -350,9 +382,10 @@ namespace CodeWalker.Rendering //instanced rendering of all other lights, using appropriate shapes //blend mode: additive + var ps = (MSAASampleCount > 1) ? LodLightMSPS : LodLightPS; context.VertexShader.Set(LodLightVS); - context.PixelShader.Set(LodLightPS); + context.PixelShader.Set(ps); LightVSVars.Vars.ViewProj = Matrix.Transpose(camera.ViewProjMatrix); LightVSVars.Vars.CameraPos = new Vector4(camera.Position, 0.0f); @@ -361,26 +394,20 @@ namespace CodeWalker.Rendering LightVSVars.Vars.Pad0 = 0; LightVSVars.Vars.Pad1 = 0; - //LightPSVars.Vars.GlobalLights = globalLights.Params; LightPSVars.Vars.ViewProjInv = Matrix.Transpose(camera.ViewProjInvMatrix); LightPSVars.Vars.CameraPos = new Vector4(camera.Position, 0.0f); - LightPSVars.Vars.EnableShadows = 0;// (globalShadows != null) ? 1u : 0u; - LightPSVars.Vars.RenderMode = 0;// rendermode; - LightPSVars.Vars.RenderModeIndex = 1;// rendermodeind; - LightPSVars.Vars.RenderSamplerCoord = 0;// (uint)RenderTextureSamplerCoord; + LightPSVars.Vars.EnableShadows = 0; + LightPSVars.Vars.RenderMode = 0; + LightPSVars.Vars.RenderModeIndex = 1; + LightPSVars.Vars.RenderSamplerCoord = 0; LightPSVars.Vars.LightType = 0; LightPSVars.Vars.IsLOD = 0; - LightPSVars.Vars.Pad0 = 0; - LightPSVars.Vars.Pad1 = 0; + LightPSVars.Vars.SampleCount = (uint)MSAASampleCount; + LightPSVars.Vars.SampleMult = 1.0f / MSAASampleCount; context.PixelShader.SetShaderResources(0, GBuffers.DepthSRV); context.PixelShader.SetShaderResources(2, GBuffers.SRVs); - //if (globalShadows != null) - //{ - // globalShadows.SetFinalRenderResources(context); - //} - foreach (var rll in lodlights) { @@ -427,10 +454,6 @@ namespace CodeWalker.Rendering context.PixelShader.Set(null); context.PixelShader.SetShaderResources(0, null, null, null); context.PixelShader.SetSamplers(0, null, null); - - - - } diff --git a/Rendering/Utils/GpuBuffers.cs b/Rendering/Utils/GpuBuffers.cs index d67cf32..6f3b462 100644 --- a/Rendering/Utils/GpuBuffers.cs +++ b/Rendering/Utils/GpuBuffers.cs @@ -293,13 +293,13 @@ namespace CodeWalker.Rendering TextureMS = DXUtility.CreateTexture2D(device, w, h, 1, 1, f, sc, sq, u, b, 0, 0); MSRTV = DXUtility.CreateRenderTargetView(device, TextureMS, f, rtvd, 0, 0, 0); - VramUsage += (wh * fs); + VramUsage += (wh * fs) * sc; if (depth) { DepthMS = DXUtility.CreateTexture2D(device, w, h, 1, 1, df, sc, sq, u, db, 0, 0); MSDSV = DXUtility.CreateDepthStencilView(device, DepthMS, df, dsvd); - VramUsage += (wh * DXUtility.ElementSize(df)); + VramUsage += (wh * DXUtility.ElementSize(df)) * sc; } } else @@ -432,31 +432,42 @@ namespace CodeWalker.Rendering public int VramUsage; public bool UseDepth; public int Count; + public bool Multisampled; + public int MultisampleCount; - public void Init(Device device, int w, int h, int count, Format f, bool depth, Format df) + public void Init(Device device, int w, int h, int count, Format f, bool depth, Format df, int multisamplecount) { Count = count; VramUsage = 0; UseDepth = depth; + MultisampleCount = multisamplecount; + Multisampled = (multisamplecount > 1); ResourceUsage u = ResourceUsage.Default; BindFlags b = BindFlags.RenderTarget | BindFlags.ShaderResource; - RenderTargetViewDimension rtvd = RenderTargetViewDimension.Texture2D; - ShaderResourceViewDimension srvd = ShaderResourceViewDimension.Texture2D;// D3D11_SRV_DIMENSION_TEXTURE2D; int fs = DXUtility.ElementSize(f); int wh = w * h; BindFlags db = BindFlags.DepthStencil | BindFlags.ShaderResource;// D3D11_BIND_DEPTH_STENCIL; + RenderTargetViewDimension rtvd = RenderTargetViewDimension.Texture2D; + ShaderResourceViewDimension srvd = ShaderResourceViewDimension.Texture2D;// D3D11_SRV_DIMENSION_TEXTURE2D; DepthStencilViewDimension dsvd = DepthStencilViewDimension.Texture2D; + if (Multisampled) + { + rtvd = RenderTargetViewDimension.Texture2DMultisampled; + srvd = ShaderResourceViewDimension.Texture2DMultisampled; + dsvd = DepthStencilViewDimension.Texture2DMultisampled; + } + Textures = new Texture2D[count]; RTVs = new RenderTargetView[count]; SRVs = new ShaderResourceView[count]; for (int i = 0; i < count; i++) { - Textures[i] = DXUtility.CreateTexture2D(device, w, h, 1, 1, f, 1, 0, u, b, 0, 0); + Textures[i] = DXUtility.CreateTexture2D(device, w, h, 1, 1, f, multisamplecount, 0, u, b, 0, 0); RTVs[i] = DXUtility.CreateRenderTargetView(device, Textures[i], f, rtvd, 0, 0, 0); SRVs[i] = DXUtility.CreateShaderResourceView(device, Textures[i], f, srvd, 1, 0, 0, 0); - VramUsage += (wh * fs); + VramUsage += (wh * fs) * multisamplecount; } if (depth) { @@ -482,10 +493,10 @@ namespace CodeWalker.Rendering break; } - Depth = DXUtility.CreateTexture2D(device, w, h, 1, 1, dtexf, 1, 0, u, db, 0, 0); + Depth = DXUtility.CreateTexture2D(device, w, h, 1, 1, dtexf, multisamplecount, 0, u, db, 0, 0); DSV = DXUtility.CreateDepthStencilView(device, Depth, df, dsvd); DepthSRV = DXUtility.CreateShaderResourceView(device, Depth, dsrvf, srvd, 1, 0, 0, 0); - VramUsage += (wh * DXUtility.ElementSize(df)); + VramUsage += (wh * DXUtility.ElementSize(df)) * multisamplecount; } } public void Dispose() @@ -516,13 +527,13 @@ namespace CodeWalker.Rendering Depth = null; } } - public GpuMultiTexture(Device device, int w, int h, int count, Format f, bool depth, Format df) + public GpuMultiTexture(Device device, int w, int h, int count, Format f, bool depth, Format df, int msc = 1) { - Init(device, w, h, count, f, depth, df); + Init(device, w, h, count, f, depth, df, msc); } - public GpuMultiTexture(Device device, int w, int h, int count, Format f) + public GpuMultiTexture(Device device, int w, int h, int count, Format f, int msc = 1) { - Init(device, w, h, count, f, false, Format.Unknown); + Init(device, w, h, count, f, false, Format.Unknown, msc); } public void Clear(DeviceContext context, Color4 colour) diff --git a/Shaders/DirLightPS.cso b/Shaders/DirLightPS.cso index ff652f9b958613b7e0df3deabbf2fa057b0cc33b..470694748b1dabcea8f49eb995a204feb9ed6ff9 100644 GIT binary patch delta 140 zcmZ2swZ+QHCBn&h#qFn#|01URQQJ2^C0{D)Fe3v4gNZBy11pFIVjB(yhABY&0*H6W zFfatUxVkZLOtg~XJj)K01uAEr?8qp-vE&G=&>^769iSc-AO?XYEStU9RxpYMC*~I9 bq&nx9=9Mr&*uJGXC6j-%n`}1V_#pxSUIHc1 delta 127 zcmdmDwZh8ECBn)1S)BeE)yTg$K8pTK3}5v~l97RdK|_{-ffY!b0I>lF149Q8-vQz^ zG7JnsF0O80*e6;^aUNi2V6XuyXP)fHD88}e2&>R;pvWDd9u^=5feiM|UTiBEIRg?? S3>bif;baaDlg-u~KScnCLLW5% diff --git a/Shaders/DirLightPS_MS.cso b/Shaders/DirLightPS_MS.cso new file mode 100644 index 0000000000000000000000000000000000000000..240b300af12889a1f4c7d7431e206abdc5f02132 GIT binary patch literal 40856 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 P^Ep`=KWtvgDXahhOkOt& delta 268 zcmX>Ryd&7wCBn&hE0e5X_#eZWNAE{HNqeermXU#hVTUFI0~?UG0OC8G3=9!m3=C_4 zSO+L>1H?ftu5NQC+DbEKY>a%($jGqSmC2li^*1|E^<+gh;muRn7BNZ(B&HZJ00~0| z2@aq+Ko9`LOh6nlIg#Uhy&_Ol2P$I&q+NiR2Phi?6&C=q+930m^m(vJXJn9zgbKDBB0fW(8tK29e3@IawJWY~IQ#tN;Lt%r9>M diff --git a/Shaders/LodLightsPS.cso b/Shaders/LodLightsPS.cso index 905872e546f51a63c2868a018cc5799429f1ca08..b95822b37c5a370d22c5361cf55b433bcc3a061d 100644 GIT binary patch delta 309 zcmeyMF+tPTCBn(syTQlBX6M@juePk4ZxyWYfRTZLVS)$)0~?UG0AdXwV+j!70Ad!P zxCjsjxwyK$m}o1_xMgFcG9#nU^Yy+!(Fmwa29PcQVjiGu15{i9$nJuQ%K+JPfb11OtN>*1fU*sM u?1Mn|86b85vhP6IE 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 literal 0 HcmV?d00001