using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Device = SharpDX.Direct3D11.Device; using Buffer = SharpDX.Direct3D11.Buffer; using MapFlags = SharpDX.Direct3D11.MapFlags; using SharpDX.Direct3D11; using System.IO; using CodeWalker.GameFiles; using CodeWalker.World; using SharpDX; using SharpDX.DXGI; using SharpDX.Mathematics.Interop; namespace CodeWalker.Rendering { public struct PostProcessorReduceCSVars { public uint dimx; public uint dimy; public uint Width; public uint Height; } public struct PostProcessorLumBlendCSVars { public Vector4 blend; } public struct PostProcessorFilterSampleWeights { public Vector4 avSampleWeights00; //[15]; public Vector4 avSampleWeights01; public Vector4 avSampleWeights02; public Vector4 avSampleWeights03; public Vector4 avSampleWeights04; public Vector4 avSampleWeights05; public Vector4 avSampleWeights06; public Vector4 avSampleWeights07; public Vector4 avSampleWeights08; public Vector4 avSampleWeights09; public Vector4 avSampleWeights10; public Vector4 avSampleWeights11; public Vector4 avSampleWeights12; public Vector4 avSampleWeights13; public Vector4 avSampleWeights14; public Vector4 Get(int i) { switch (i) { default: case 0: return avSampleWeights00; case 1: return avSampleWeights01; case 2: return avSampleWeights02; case 3: return avSampleWeights03; case 4: return avSampleWeights04; case 5: return avSampleWeights05; case 6: return avSampleWeights06; case 7: return avSampleWeights07; case 8: return avSampleWeights08; case 9: return avSampleWeights09; case 10: return avSampleWeights10; case 11: return avSampleWeights11; case 12: return avSampleWeights12; case 13: return avSampleWeights13; case 14: return avSampleWeights14; } } public void Set(int i, Vector4 v) { switch (i) { case 0: avSampleWeights00 = v; break; case 1: avSampleWeights01 = v; break; case 2: avSampleWeights02 = v; break; case 3: avSampleWeights03 = v; break; case 4: avSampleWeights04 = v; break; case 5: avSampleWeights05 = v; break; case 6: avSampleWeights06 = v; break; case 7: avSampleWeights07 = v; break; case 8: avSampleWeights08 = v; break; case 9: avSampleWeights09 = v; break; case 10: avSampleWeights10 = v; break; case 11: avSampleWeights11 = v; break; case 12: avSampleWeights12 = v; break; case 13: avSampleWeights13 = v; break; case 14: avSampleWeights14 = v; break; } } } public struct PostProcessorFilterBPHCSVars { public PostProcessorFilterSampleWeights avSampleWeights; public uint outputwidth; public float finverse; public int inputsize0; public int inputsize1; } public struct PostProcessorFilterVCSVars { public PostProcessorFilterSampleWeights avSampleWeights; public int outputsize0; public int outputsize1; public int inputsize0; public int inputsize1; } public struct PostProcessorFinalPSVars { public Vector4 invPixelCount; } public class PostProcessor { ComputeShader ReduceTo1DCS; ComputeShader ReduceTo0DCS; ComputeShader LumBlendCS; ComputeShader BloomFilterBPHCS; ComputeShader BloomFilterVCS; PixelShader CopyPixelsPS; VertexShader FinalPassVS; PixelShader FinalPassPS; UnitQuad FinalPassQuad; InputLayout FinalPassLayout; GpuVarsBuffer ReduceCSVars; GpuVarsBuffer LumBlendCSVars; GpuVarsBuffer FilterBPHCSVars; GpuVarsBuffer FilterVCSVars; GpuVarsBuffer FinalPSVars; GpuTexture Primary; GpuBuffer Reduction0; GpuBuffer Reduction1; GpuBuffer LumBlendResult; GpuBuffer Bloom0; GpuBuffer Bloom1; GpuTexture Bloom; SamplerState SampleStatePoint; SamplerState SampleStateLinear; BlendState BlendState; long WindowSizeVramUsage = 0; int Width = 0; int Height = 0; ViewportF Viewport; bool Multisampled = false; bool EnableBloom = true; float ElapsedTime = 0.0f; public float LumBlendSpeed = 1.0f; bool CS_FULL_PIXEL_REDUCTION = false; public long VramUsage { get { return WindowSizeVramUsage; } } RawViewportF[] vpOld = new RawViewportF[15]; public PostProcessor(DXManager dxman) { var device = dxman.device; byte[] bReduceTo1DCS = File.ReadAllBytes("Shaders\\PPReduceTo1DCS.cso"); byte[] bReduceTo0DCS = File.ReadAllBytes("Shaders\\PPReduceTo0DCS.cso"); byte[] bLumBlendCS = File.ReadAllBytes("Shaders\\PPLumBlendCS.cso"); byte[] bBloomFilterBPHCS = File.ReadAllBytes("Shaders\\PPBloomFilterBPHCS.cso"); byte[] bBloomFilterVCS = File.ReadAllBytes("Shaders\\PPBloomFilterVCS.cso"); byte[] bCopyPixelsPS = File.ReadAllBytes("Shaders\\PPCopyPixelsPS.cso"); byte[] bFinalPassVS = File.ReadAllBytes("Shaders\\PPFinalPassVS.cso"); byte[] bFinalPassPS = File.ReadAllBytes("Shaders\\PPFinalPassPS.cso"); ReduceTo1DCS = new ComputeShader(device, bReduceTo1DCS); ReduceTo0DCS = new ComputeShader(device, bReduceTo0DCS); LumBlendCS = new ComputeShader(device, bLumBlendCS); BloomFilterBPHCS = new ComputeShader(device, bBloomFilterBPHCS); BloomFilterVCS = new ComputeShader(device, bBloomFilterVCS); CopyPixelsPS = new PixelShader(device, bCopyPixelsPS); FinalPassVS = new VertexShader(device, bFinalPassVS); FinalPassPS = new PixelShader(device, bFinalPassPS); FinalPassQuad = new UnitQuad(device, true); FinalPassLayout = new InputLayout(device, bFinalPassVS, new[] { new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), new InputElement("TEXCOORD", 0, Format.R32G32_Float, 16, 0), }); ReduceCSVars = new GpuVarsBuffer(device); LumBlendCSVars = new GpuVarsBuffer(device); FilterBPHCSVars = new GpuVarsBuffer(device); FilterVCSVars = new GpuVarsBuffer(device); FinalPSVars = new GpuVarsBuffer(device); TextureAddressMode a = TextureAddressMode.Clamp; Color4 b = new Color4(0.0f, 0.0f, 0.0f, 0.0f); Comparison c = Comparison.Always; SampleStatePoint = DXUtility.CreateSamplerState(device, a, b, c, Filter.MinMagMipPoint, 0, 1.0f, 1.0f, 0.0f); SampleStateLinear = DXUtility.CreateSamplerState(device, a, b, c, Filter.MinMagMipLinear, 0, 1.0f, 1.0f, 0.0f); BlendState = DXUtility.CreateBlendState(device, false, BlendOperation.Add, BlendOption.One, BlendOption.Zero, BlendOperation.Add, BlendOption.One, BlendOption.Zero, ColorWriteMaskFlags.All); GetSampleWeights(ref FilterVCSVars.Vars.avSampleWeights, 3.0f, 1.25f); //init sample weights FilterBPHCSVars.Vars.avSampleWeights = FilterVCSVars.Vars.avSampleWeights; } public void Dispose() { DisposeBuffers(); if (BlendState != null) { BlendState.Dispose(); BlendState = null; } if (SampleStateLinear != null) { SampleStateLinear.Dispose(); SampleStateLinear = null; } if (SampleStatePoint != null) { SampleStatePoint.Dispose(); SampleStatePoint = null; } if (FinalPSVars != null) { FinalPSVars.Dispose(); FinalPSVars = null; } if (FilterVCSVars != null) { FilterVCSVars.Dispose(); FilterVCSVars = null; } if (FilterBPHCSVars != null) { FilterBPHCSVars.Dispose(); FilterBPHCSVars = null; } if (LumBlendCSVars != null) { LumBlendCSVars.Dispose(); LumBlendCSVars = null; } if (ReduceCSVars != null) { ReduceCSVars.Dispose(); ReduceCSVars = null; } if (FinalPassLayout != null) { FinalPassLayout.Dispose(); FinalPassLayout = null; } if (FinalPassQuad != null) { FinalPassQuad.Dispose(); FinalPassQuad = null; } if (FinalPassPS != null) { FinalPassPS.Dispose(); FinalPassPS = null; } if (FinalPassVS != null) { FinalPassVS.Dispose(); FinalPassVS = null; } if (CopyPixelsPS != null) { CopyPixelsPS.Dispose(); CopyPixelsPS = null; } if (BloomFilterVCS != null) { BloomFilterVCS.Dispose(); BloomFilterVCS = null; } if (BloomFilterBPHCS != null) { BloomFilterBPHCS.Dispose(); BloomFilterBPHCS = null; } if (LumBlendCS != null) { LumBlendCS.Dispose(); LumBlendCS = null; } if (ReduceTo0DCS != null) { ReduceTo0DCS.Dispose(); ReduceTo0DCS = null; } if (ReduceTo1DCS != null) { ReduceTo1DCS.Dispose(); ReduceTo1DCS = null; } } public void OnWindowResize(DXManager dxman) { DisposeBuffers(); var device = dxman.device; int sc = dxman.multisamplecount; int sq = dxman.multisamplequality; Multisampled = (sc > 1); int uw = Width = dxman.backbuffer.Description.Width; int uh = Height = dxman.backbuffer.Description.Height; Viewport = new ViewportF(); Viewport.Width = (float)uw; Viewport.Height = (float)uh; Viewport.MinDepth = 0.0f; Viewport.MaxDepth = 1.0f; Viewport.X = 0.0f; Viewport.Y = 0.0f; Format f = Format.R32G32B32A32_Float; Format df = Format.D32_Float; Primary = new GpuTexture(device, uw, uh, f, sc, sq, true, df); WindowSizeVramUsage += Primary.VramUsage; int rc = (int)(Math.Ceiling(uw / 8.0f) * Math.Ceiling(uh / 8.0f)); Reduction0 = new GpuBuffer(device, 1, rc); Reduction1 = new GpuBuffer(device, 1, rc); WindowSizeVramUsage += sizeof(float) * rc * 2; LumBlendResult = new GpuBuffer(device, 1, 1); WindowSizeVramUsage += sizeof(float); //because 4 bytes matter int tw = uw / 8; int th = uh / 8; rc = tw * th; f = Format.R8G8B8A8_UNorm; Bloom0 = new GpuBuffer(device, 1, rc); Bloom1 = new GpuBuffer(device, 1, rc); WindowSizeVramUsage += /*sizeof(V4F)*/ 16 * rc * 2; Bloom = new GpuTexture(device, tw, th, f, 1, 0, false, df); WindowSizeVramUsage += Bloom.VramUsage; } public void DisposeBuffers() { if (Bloom != null) { Bloom.Dispose(); Bloom = null; } if (Bloom0 != null) { Bloom0.Dispose(); Bloom0 = null; } if (Bloom1 != null) { Bloom1.Dispose(); Bloom1 = null; } if (LumBlendResult != null) { LumBlendResult.Dispose(); LumBlendResult = null; } if (Reduction0 != null) { Reduction0.Dispose(); Reduction0 = null; } if (Reduction1 != null) { Reduction1.Dispose(); Reduction1 = null; } if (Primary != null) { Primary.Dispose(); Primary = null; } WindowSizeVramUsage = 0; } public void Clear(DeviceContext context) { Color4 clearColour = new Color4(0.2f, 0.4f, 0.6f, 0.0f); //Color4 clearColour = new Color4(0.0f, 0.0f, 0.0f, 0.0f); Primary.Clear(context, clearColour); } public void ClearDepth(DeviceContext context) { Primary.ClearDepth(context); } public void SetPrimary(DeviceContext context) { Primary.SetRenderTarget(context); context.Rasterizer.SetViewport(Viewport); } public void Render(DXManager dxman, float elapsed) { ElapsedTime = elapsed; var context = dxman.context; if (Multisampled) { int sr = 0;// D3D11CalcSubresource(0, 0, 1); context.ResolveSubresource(Primary.TextureMS, sr, Primary.Texture, sr, Format.R32G32B32A32_Float); } context.OutputMerger.SetRenderTargets((RenderTargetView)null); ProcessLuminance(context); ProcessBloom(context); dxman.SetDefaultRenderTarget(context); context.OutputMerger.SetBlendState(BlendState, null, 0xFFFFFFFF); FinalPass(context); } private void ProcessLuminance(DeviceContext context) { uint dimx, dimy; if (CS_FULL_PIXEL_REDUCTION) { dimx = (uint)(Math.Ceiling(Width / 8.0f)); dimx = (uint)(Math.Ceiling(dimx / 2.0f)); dimy = (uint)(Math.Ceiling(Height / 8.0f)); dimy = (uint)(Math.Ceiling(dimy / 2.0f)); } else { dimx = (uint)(Math.Ceiling(81.0f / 8.0f)); //ToneMappingTexSize = (int)pow(3.0f, NUM_TONEMAP_TEXTURES-1); dimy = dimx; } ReduceCSVars.Vars.dimx = dimx; ReduceCSVars.Vars.dimy = dimy; ReduceCSVars.Vars.Width = (uint)Width; ReduceCSVars.Vars.Height = (uint)Height; ReduceCSVars.Update(context); Compute(context, ReduceTo1DCS, ReduceCSVars.Buffer, Reduction0.UAV, (int)dimx, (int)dimy, 1, Primary.SRV); uint dim = dimx * dimy; uint nNumToReduce = dim; dim = (uint)(Math.Ceiling(dim / 128.0f)); if (nNumToReduce > 1) { for (;;) { ReduceCSVars.Vars.dimx = nNumToReduce; ReduceCSVars.Vars.dimy = dim; ReduceCSVars.Vars.Width = 0; ReduceCSVars.Vars.Height = 0; ReduceCSVars.Update(context); Compute(context, ReduceTo0DCS, ReduceCSVars.Buffer, Reduction1.UAV, (int)dim, 1, 1, Reduction0.SRV); nNumToReduce = dim; dim = (uint)(Math.Ceiling(dim / 128.0f)); if (nNumToReduce == 1) break; var r0 = Reduction0; Reduction0 = Reduction1; Reduction1 = r0; } } else { var r0 = Reduction0; Reduction0 = Reduction1; Reduction1 = r0; } LumBlendCSVars.Vars.blend = new Vector4(Math.Min(ElapsedTime * LumBlendSpeed, 1.0f)); LumBlendCSVars.Update(context); Compute(context, LumBlendCS, LumBlendCSVars.Buffer, LumBlendResult.UAV, 1, 1, 1, Reduction1.SRV); } private void ProcessBloom(DeviceContext context) { if (EnableBloom) { // Bright pass and horizontal blur ShaderResourceView view = Primary.SRV; ShaderResourceView[] aRViews = { view, LumBlendResult.SRV }; //BloomFilterShaderVars cbFilter; //GetSampleWeights(cbFilter.avSampleWeights, 3.0f, 1.25f); FilterBPHCSVars.Vars.outputwidth = (uint)(Width / 8); if (CS_FULL_PIXEL_REDUCTION) { FilterBPHCSVars.Vars.finverse = 1.0f / (Width * Height); } else { FilterBPHCSVars.Vars.finverse = 1.0f / (81 * 81); //(ToneMappingTexSize*ToneMappingTexSize); } FilterBPHCSVars.Vars.inputsize0 = (int)Width; FilterBPHCSVars.Vars.inputsize1 = (int)Height; FilterBPHCSVars.Update(context); int x = (int)(Math.Ceiling((float)FilterBPHCSVars.Vars.outputwidth / (128 - 7 * 2))); int y = (Height / 8); Compute(context, BloomFilterBPHCS, FilterBPHCSVars.Buffer, Bloom1.UAV, x, y, 1, view, LumBlendResult.SRV); // Vertical blur FilterVCSVars.Vars.outputsize0 = (int)(Width / 8); FilterVCSVars.Vars.outputsize1 = (int)(Height / 8); FilterVCSVars.Vars.inputsize0 = (int)(Width / 8); FilterVCSVars.Vars.inputsize1 = (int)(Height / 8); FilterVCSVars.Update(context); x = Width / 8; y = (int)(Math.Ceiling((float)FilterVCSVars.Vars.outputsize1 / (128 - 7 * 2))); Compute(context, BloomFilterVCS, FilterVCSVars.Buffer, Bloom0.UAV, x, y, 1, Bloom1.SRV, LumBlendResult.SRV); } CopyPixels(context, Width / 8, Height / 8, Bloom0.SRV, Bloom.RTV); } private void FinalPass(DeviceContext context) { context.Rasterizer.SetViewport(Viewport); context.VertexShader.Set(FinalPassVS); context.PixelShader.Set(FinalPassPS); context.PixelShader.SetShaderResources(0, Primary.SRV, LumBlendResult.SRV, EnableBloom ? Bloom.SRV : null); if (CS_FULL_PIXEL_REDUCTION) { //pcbCS[0] = 1.0f / (Width * Height); FinalPSVars.Vars.invPixelCount = new Vector4(1.0f / (Width * Height)); } else { //pcbCS[0] = 1.0f / (81 * 81); //ToneMappingTexSize*ToneMappingTexSize FinalPSVars.Vars.invPixelCount = new Vector4(1.0f / (81 * 81)); } FinalPSVars.Update(context); FinalPSVars.SetPSCBuffer(context, 0); context.PixelShader.SetSamplers(0, SampleStatePoint, SampleStateLinear); context.InputAssembler.InputLayout = FinalPassLayout; FinalPassQuad.Draw(context); context.VertexShader.Set(null); context.PixelShader.Set(null); context.PixelShader.SetShaderResources(0, null, null, null); context.PixelShader.SetSamplers(0, null, null); } private float GaussianDistribution(float x, float y, float rho) { float g = 1.0f / (float)Math.Sqrt(2.0f * 3.14159265f * rho * rho); g *= (float)Math.Exp(-(x * x + y * y) / (2 * rho * rho)); return g; } private void GetSampleWeights(ref PostProcessorFilterSampleWeights w, float fDeviation, float fMultiplier) { // Fill the center texel float weight = 1.0f * GaussianDistribution(0, 0, fDeviation); w.Set(7, new Vector4(weight, weight, weight, 1.0f)); // Fill the right side for (int i = 1; i < 8; i++) { weight = fMultiplier * GaussianDistribution((float)i, 0, fDeviation); w.Set(7 - i, new Vector4(weight, weight, weight, 1.0f)); } // Copy to the left side for (int i = 8; i < 15; i++) { w.Set(i, w.Get(14 - i)); } // Debug convolution kernel which doesn't transform input data /*ZeroMemory( avColorWeight, sizeof(D3DXVECTOR4)*15 ); w.Set(7, new Vector4( 1, 1, 1, 1 ));*/ } private void Compute(DeviceContext context, ComputeShader cs, Buffer constantBuffer, UnorderedAccessView unorderedAccessView, int X, int Y, int Z, params ShaderResourceView[] resourceViews) { context.ComputeShader.Set(cs); context.ComputeShader.SetShaderResources(0, resourceViews); context.ComputeShader.SetUnorderedAccessView(0, unorderedAccessView); if (constantBuffer != null) { //make sure buffer is updated first... //D3D11_MAPPED_SUBRESOURCE mappedResource; //dc->Map(constantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); //memcpy(mappedResource.pData, constantData, constantDataSize); //dc->Unmap(constantBuffer, 0); context.ComputeShader.SetConstantBuffer(0, constantBuffer); //dc->CSSetConstantBuffers(0, 1, &constantBuffer); } context.Dispatch(X, Y, Z); ShaderResourceView[] ppSRVNULL = { null, null, null }; context.ComputeShader.SetUnorderedAccessView(0, null); context.ComputeShader.SetShaderResources(0, 3, ppSRVNULL); context.ComputeShader.SetConstantBuffer(0, null); } private void CopyPixels(DeviceContext context, int w, int h, ShaderResourceView fromSRV, RenderTargetView toRTV) { context.VertexShader.Set(FinalPassVS); context.PixelShader.Set(CopyPixelsPS); ShaderResourceView[] aRViews = { fromSRV }; context.PixelShader.SetShaderResource(0, fromSRV); RenderTargetView[] aRTViews = { toRTV }; context.OutputMerger.SetRenderTargets(toRTV); //D3D11_MAPPED_SUBRESOURCE mappedResource; //dc->Map(ReduceCSVars.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); //UINT* p = (UINT*)mappedResource.pData; //p[0] = w; //p[1] = h; //dc->Unmap(ReduceCSVars.Get(), 0); //ID3D11Buffer* ppCB[1] = { ReduceCSVars.Get() }; //dc->PSSetConstantBuffers(0, 1, ppCB); ReduceCSVars.Vars.dimx = (uint)w; ReduceCSVars.Vars.dimy = (uint)h; ReduceCSVars.Update(context); ReduceCSVars.SetPSCBuffer(context, 0); //DrawFullScreenQuad11( pd3dImmediateContext, g_pDumpBufferPS, dwWidth, dwHeight ); //ViewportF[] vpOld = new ViewportF[15];// [D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX]; //UINT nViewPorts = 1; //dc->RSGetViewports(&nViewPorts, vpOld); context.Rasterizer.GetViewports(vpOld); // Setup the viewport to match the backbuffer ViewportF vp; vp.Width = (float)Width; vp.Height = (float)Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.X = 0; vp.Y = 0; context.Rasterizer.SetViewport(vp); context.InputAssembler.InputLayout = FinalPassLayout; FinalPassQuad.Draw(context); context.Rasterizer.SetViewports(vpOld); //reverting viewports maybe not necessary... } } }