using System; using System.Numerics; using System.Runtime.InteropServices; using TerraFX.Interop.DirectX; namespace Auspex.Rendering.Direct3D; internal sealed class ClipZoneShader : IDisposable { public struct Constants { public Vector2 ViewportSize; } public struct Instance { public Vector2 Min; public Vector2 Max; } private readonly FrameRenderContext _ctx; private readonly GpuBuffer _buffer; private GpuBuffer.Builder? _builder; private unsafe ID3D11Buffer* _constantBuffer; private unsafe ID3D11InputLayout* _il; private unsafe ID3D11VertexShader* _vs; private unsafe ID3D11PixelShader* _ps; public bool HasPending => _builder != null; public unsafe ClipZoneShader(FrameRenderContext ctx, int maxRects) { _ctx = ctx; _buffer = new GpuBuffer("ClipZone", ctx, maxRects, 1u, dynamic: true); ReadOnlySpan source = "cbuffer Constants : register(b0)\n{\n float2 viewportSize;\n};\n\nstruct Rect\n{\n float2 rmin : RECTMIN;\n float2 rmax : RECTMAX;\n};\n\nstruct VSOutput\n{\n float4 pos : SV_POSITION;\n};\n\nVSOutput vs(in Rect r, uint vid : SV_VertexID)\n{\n VSOutput o;\n float2 px = float2(\n (vid & 1) ? r.rmax.x : r.rmin.x,\n (vid & 2) ? r.rmax.y : r.rmin.y);\n float2 ndc = float2(\n px.x / viewportSize.x * 2.0 - 1.0,\n 1.0 - px.y / viewportSize.y * 2.0);\n o.pos = float4(ndc, 0, 1);\n return o;\n}\n\nvoid ps(VSOutput input)\n{\n}"u8; TriangleFillShader.CompileShader(source, "vs"u8, "vs_5_0"u8, out var blob, "ClipZone VS"); TriangleFillShader.CompileShader(source, "ps"u8, "ps_5_0"u8, out var blob2, "ClipZone 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; fixed (byte* semanticName = "RECTMIN"u8) { fixed (byte* semanticName2 = "RECTMAX"u8) { D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[2]; *ptr = new D3D11_INPUT_ELEMENT_DESC { SemanticName = (sbyte*)semanticName, SemanticIndex = 0u, Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT, AlignedByteOffset = uint.MaxValue, InputSlot = 0u, InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA, InstanceDataStepRate = 1u }; ptr[1] = new D3D11_INPUT_ELEMENT_DESC { SemanticName = (sbyte*)semanticName2, SemanticIndex = 0u, Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT, AlignedByteOffset = uint.MaxValue, InputSlot = 0u, InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA, InstanceDataStepRate = 1u }; ID3D11InputLayout* il = default(ID3D11InputLayout*); Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 2u, blob->GetBufferPointer(), blob->GetBufferSize(), &il)); _il = il; } } blob->Release(); blob2->Release(); } public unsafe void Dispose() { _builder?.Dispose(); _buffer.Dispose(); if (_constantBuffer != null) { _constantBuffer->Release(); _constantBuffer = null; } if (_il != null) { _il->Release(); _il = null; } if (_vs != null) { _vs->Release(); _vs = null; } if (_ps != null) { _ps->Release(); _ps = null; } GC.SuppressFinalize(this); } public unsafe void UpdateConstants(Constants consts) { _ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u); } public void Add(Vector2 min, Vector2 max) { if (_builder == null) { _builder = _buffer.Map(_ctx); } Instance item = new Instance { Min = min, Max = max }; _builder.Add(ref item); } public unsafe void Flush() { if (_builder != null) { _builder.Dispose(); _builder = null; Bind(); ID3D11Buffer* buffer = _buffer.Buffer; uint elementSize = (uint)_buffer.ElementSize; uint num = 0u; _ctx.Context->IASetVertexBuffers(0u, 1u, &buffer, &elementSize, &num); _ctx.Context->DrawInstanced(4u, (uint)_buffer.CurElements, 0u, 0u); } } public void Discard() { if (_builder != null) { _builder.Dispose(); _builder = null; } } private unsafe void Bind() { _ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); _ctx.Context->IASetInputLayout(_il); _ctx.Context->VSSetShader(_vs, null, 0u); ID3D11Buffer* constantBuffer = _constantBuffer; _ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer); _ctx.Context->PSSetShader(_ps, null, 0u); _ctx.Context->GSSetShader(null, null, 0u); } }