using System; using System.Runtime.InteropServices; using TerraFX.Interop.DirectX; namespace Auspex.Rendering.Direct3D; internal sealed class FullscreenPassShader : IDisposable { public struct Constants { public float MaxAlpha; public float ClipNativeUI; } private unsafe ID3D11Buffer* _constantBuffer; private unsafe ID3D11VertexShader* _vs; private unsafe ID3D11PixelShader* _ps; public unsafe FullscreenPassShader(FrameRenderContext ctx) { ReadOnlySpan source = "struct Constants\n{\n float maxAlpha;\n float clipNativeUI;\n};\nConstants k : register(b0);\n\nTexture2D inputTexture : register(t0);\nTexture2D maskTexture : register(t1);\n\nSamplerState TextureSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\nstruct VSOutput\n{\n float4 pos : SV_POSITION;\n float2 uv: TEXCOORD;\n};\n\nVSOutput vs(uint id : SV_VertexID)\n{\n VSOutput output;\n\tfloat2 uv = float2((id << 1) & 2, id & 2);\n\toutput.pos = float4(uv * float2(2, -2) + float2(-1, 1), 0, 1);\n output.uv = uv;\n return output;\n}\n\nfloat4 ps(VSOutput input) : SV_Target\n{\n float4 color = inputTexture.Sample(TextureSampler, input.uv);\n if (color.a > 0)\n {\n color.rgb /= color.a;\n }\n float maskAlpha = 1;\n if (k.clipNativeUI > 0.5)\n {\n float4 mask = maskTexture.Sample(TextureSampler, input.uv);\n // Apply mask alpha squared\n // (I don't think this is mathematically correct but it looks better)\n maskAlpha = 1 - mask.a;\n maskAlpha *= maskAlpha;\n }\n color.a = min(color.a, k.maxAlpha) * maskAlpha;\n return color;\n}"u8; TriangleFillShader.CompileShader(source, "vs"u8, "vs_5_0"u8, out var blob, "FSP VS"); TriangleFillShader.CompileShader(source, "ps"u8, "ps_5_0"u8, out var blob2, "FSP PS"); ID3D11VertexShader* vs = default(ID3D11VertexShader*); Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs)); _vs = vs; ID3D11PixelShader* ps = default(ID3D11PixelShader*); Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &ps)); _ps = ps; D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC { ByteWidth = 16u, Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT, BindFlags = 4u }; ID3D11Buffer* constantBuffer = default(ID3D11Buffer*); Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer)); _constantBuffer = constantBuffer; blob->Release(); blob2->Release(); } public unsafe void Dispose() { if (_constantBuffer != null) { _constantBuffer->Release(); _constantBuffer = null; } if (_vs != null) { _vs->Release(); _vs = null; } if (_ps != null) { _ps->Release(); _ps = null; } GC.SuppressFinalize(this); } public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts) { ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u); } public unsafe void Bind(FrameRenderContext ctx) { ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ctx.Context->VSSetShader(_vs, null, 0u); ctx.Context->PSSetShader(_ps, null, 0u); ID3D11Buffer* constantBuffer = _constantBuffer; ctx.Context->PSSetConstantBuffers(0u, 1u, &constantBuffer); ctx.Context->GSSetShader(null, null, 0u); } public unsafe void Draw(FrameRenderContext ctx, ID3D11ShaderResourceView* baseSRV, ID3D11ShaderResourceView* maskSRV) { ctx.Context->PSSetShaderResources(0u, 1u, &baseSRV); ctx.Context->PSSetShaderResources(1u, 1u, &maskSRV); Bind(ctx); ctx.Context->Draw(3u, 0u); ID3D11ShaderResourceView* ptr = null; ctx.Context->PSSetShaderResources(0u, 1u, &ptr); ctx.Context->PSSetShaderResources(1u, 1u, &ptr); } }