using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; using TerraFX.Interop.DirectX; namespace Auspex.Rendering.Direct3D; internal sealed class QuadBlitter : IDisposable { public struct Constants { public Matrix4x4 ViewProj; } public struct Vertex { public Vector3 Position; public Vector2 UV; public Vector4 Color; } public struct DrawSegment { public IntPtr TextureSRV; public int StartVertex; public int VertexCount; } public sealed class Data : HlslShaderData { public sealed class Builder : HlslShaderBuilder { private readonly List _segments; private IntPtr _currentTexture; private int _segmentStart; internal Builder(FrameRenderContext ctx, Data data) : base(data._buffer.Map(ctx)) { _segments = data._segments; _segments.Clear(); _currentTexture = (IntPtr)0; _segmentStart = 0; } public override void Dispose() { FinalizeSegment(); base.Dispose(); } public void SetTexture(IntPtr srv) { if (srv != _currentTexture) { FinalizeSegment(); _currentTexture = srv; _segmentStart = _inner.CurElements; } } private void FinalizeSegment() { int num = _inner.CurElements - _segmentStart; if (num > 0 && _currentTexture != (IntPtr)0) { _segments.Add(new DrawSegment { TextureSRV = _currentTexture, StartVertex = _segmentStart, VertexCount = num }); } } public void Add(Vector3 position, Vector2 uv, Vector4 color) { _inner.Add(new Vertex { Position = position, UV = uv, Color = color }); } } internal readonly List _segments = new List(); internal GpuBuffer Buffer => _buffer; public Data(FrameRenderContext ctx, int maxCount, bool dynamic) : base("QuadBlitter", ctx, maxCount, dynamic) { } public Builder Map(FrameRenderContext ctx) { return new Builder(ctx, this); } } private unsafe ID3D11Buffer* _constantBuffer; private unsafe ID3D11InputLayout* _il; private unsafe ID3D11VertexShader* _vs; private unsafe ID3D11PixelShader* _ps; private unsafe ID3D11SamplerState* _sampler; public unsafe QuadBlitter(FrameRenderContext ctx) { ReadOnlySpan source = "struct Vertex\n{\n float3 pos : POSITION;\n float2 uv : TEXCOORD;\n float4 color : COLOR;\n};\n\nstruct VSOutput\n{\n float4 projPos : SV_POSITION;\n float2 uv : TEXCOORD;\n float4 color : COLOR;\n};\n\nstruct Constants\n{\n float4x4 viewProj;\n};\nConstants k : register(c0);\n\nTexture2D tex : register(t0);\nSamplerState samp : register(s0);\n\nVSOutput vs(Vertex v)\n{\n VSOutput o;\n o.projPos = mul(float4(v.pos, 1), k.viewProj);\n o.uv = v.uv;\n o.color = v.color;\n return o;\n}\n\nfloat4 ps(VSOutput input) : SV_TARGET\n{\n return tex.Sample(samp, input.uv) * input.color;\n}"u8; TriangleFillShader.CompileShader(source, "vs"u8, "vs_5_0"u8, out var blob, "QuadBlitter VS"); TriangleFillShader.CompileShader(source, "ps"u8, "ps_5_0"u8, out var blob2, "QuadBlitter 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 = 64u, 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 = "POSITION"u8) { fixed (byte* semanticName2 = "TEXCOORD"u8) { fixed (byte* semanticName3 = "COLOR"u8) { D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[3]; *ptr = new D3D11_INPUT_ELEMENT_DESC { SemanticName = (sbyte*)semanticName, SemanticIndex = 0u, Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT, AlignedByteOffset = uint.MaxValue, InputSlot = 0u, InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA, InstanceDataStepRate = 0u }; 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_VERTEX_DATA, InstanceDataStepRate = 0u }; ptr[2] = new D3D11_INPUT_ELEMENT_DESC { SemanticName = (sbyte*)semanticName3, SemanticIndex = 0u, Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT, AlignedByteOffset = uint.MaxValue, InputSlot = 0u, InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_VERTEX_DATA, InstanceDataStepRate = 0u }; ID3D11InputLayout* il = default(ID3D11InputLayout*); Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 3u, blob->GetBufferPointer(), blob->GetBufferSize(), &il)); _il = il; } } } D3D11_SAMPLER_DESC d3D11_SAMPLER_DESC = new D3D11_SAMPLER_DESC { Filter = D3D11_FILTER.D3D11_FILTER_MIN_MAG_MIP_LINEAR, AddressU = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_CLAMP, AddressV = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_CLAMP, AddressW = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_CLAMP, MaxAnisotropy = 1u, ComparisonFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_NEVER, MaxLOD = float.MaxValue }; ID3D11SamplerState* sampler = default(ID3D11SamplerState*); Marshal.ThrowExceptionForHR(ctx.Device->CreateSamplerState(&d3D11_SAMPLER_DESC, &sampler)); _sampler = sampler; blob->Release(); blob2->Release(); } public unsafe void 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; } if (_sampler != null) { _sampler->Release(); _sampler = null; } GC.SuppressFinalize(this); } public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts) { consts.ViewProj = Matrix4x4.Transpose(consts.ViewProj); 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->IASetInputLayout(_il); ctx.Context->VSSetShader(_vs, null, 0u); ID3D11Buffer* constantBuffer = _constantBuffer; ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer); ctx.Context->PSSetShader(_ps, null, 0u); ID3D11SamplerState* sampler = _sampler; ctx.Context->PSSetSamplers(0u, 1u, &sampler); ctx.Context->GSSetShader(null, null, 0u); } public unsafe void Draw(FrameRenderContext ctx, Data data) { Bind(ctx); ID3D11Buffer* buffer = data.Buffer.Buffer; uint elementSize = (uint)data.Buffer.ElementSize; uint num = 0u; ctx.Context->IASetVertexBuffers(0u, 1u, &buffer, &elementSize, &num); foreach (DrawSegment segment in data._segments) { ID3D11ShaderResourceView* textureSRV = (ID3D11ShaderResourceView*)segment.TextureSRV; ctx.Context->PSSetShaderResources(0u, 1u, &textureSRV); ctx.Context->Draw((uint)segment.VertexCount, (uint)segment.StartVertex); } ID3D11ShaderResourceView* ptr = null; ctx.Context->PSSetShaderResources(0u, 1u, &ptr); } }