qstbak/Auspex/Auspex.Rendering.Direct3D/TriangleFillShader.cs
2026-08-17 20:25:32 +10:00

225 lines
7.7 KiB
C#

using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Auspex.Rendering.Direct3D;
internal sealed class TriangleFillShader : IDisposable
{
public struct Constants
{
public Matrix4x4 ViewProj;
public Vector2 PixelToUv;
}
public struct Instance
{
public Vector3 Point;
public Vector4 Color;
public Vector4 FadeParams;
}
public sealed class Data : HlslShaderData<Instance>
{
public sealed class Builder : HlslShaderBuilder<Instance>
{
internal Builder(FrameRenderContext ctx, Data data)
: base(data._buffer.Map(ctx))
{
}
public void Add(Vector3 world, Vector4 color, AxDxParams p)
{
_inner.Add(new Instance
{
Point = world,
Color = color,
FadeParams = new Vector4(p.OccludedAlpha, p.OcclusionTolerance, p.FadeStart, p.FadeStop)
});
}
}
public Data(FrameRenderContext ctx, int maxCount, bool dynamic)
: base("Triangle", ctx, maxCount, dynamic)
{
}
public Builder Map(FrameRenderContext ctx)
{
return new Builder(ctx, this);
}
public void DrawSubset(FrameRenderContext ctx, int firstPoint, int numPoints)
{
DrawVertices(ctx, firstPoint, numPoints);
}
public void DrawAll(FrameRenderContext ctx)
{
DrawVertices(ctx, 0, _buffer.CurElements);
}
}
private unsafe ID3D11Buffer* _constantBuffer;
private unsafe ID3D11InputLayout* _il;
private unsafe ID3D11VertexShader* _vs;
private unsafe ID3D11PixelShader* _ps;
public unsafe TriangleFillShader(FrameRenderContext ctx)
{
byte[] bytes = Encoding.UTF8.GetBytes("cbuffer Constants : register(b0)\n{\n float4x4 viewProj;\n float2 pixelToUv;\n};\n\nTexture2D<float4> _sceneDepth : register(t0);\nSamplerState _occlusionSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\n// fadeParams: x=OccludedAlpha, y=OcclusionTolerance (m), z=FadeStart (m), w=FadeStop (m).\nfloat4 applyShared(float4 color, float3 projPos, float4 fadeParams)\n{\n float2 uv = projPos.xy * pixelToUv;\n float sceneNdcZ = _sceneDepth.Sample(_occlusionSampler, uv).r;\n\n float near = viewProj._m32;\n float shapeWorldZ = near / max(projPos.z, 1e-6);\n float sceneWorldZ = near / max(sceneNdcZ, 1e-6);\n\n float behindMeters = max(shapeWorldZ - sceneWorldZ, 0.0);\n float occlusion = behindMeters <= fadeParams.y ? 1.0 : fadeParams.x;\n\n float distanceFactor = 1.0;\n if (fadeParams.w < 1e10)\n {\n float range = max(fadeParams.w - fadeParams.z, 1e-4);\n distanceFactor = saturate((fadeParams.w - shapeWorldZ) / range);\n }\n\n color.a *= occlusion * distanceFactor;\n return color;\n}\n\nstruct Point\n{\n float3 pos : WORLD;\n float4 color : COLOR;\n float4 fadeParams : FADEPARAMS;\n};\n\nstruct VSOutput\n{\n float4 projPos : SV_POSITION;\n float4 color : COLOR;\n float4 fadeParams : FADEPARAMS;\n};\n\nVSOutput vs(Point v)\n{\n VSOutput vs;\n vs.projPos = mul(float4(v.pos, 1), viewProj);\n vs.color = v.color;\n vs.fadeParams = v.fadeParams;\n return vs;\n}\n\nfloat4 ps(VSOutput input) : SV_TARGET\n{\n return applyShared(input.color, input.projPos.xyz, input.fadeParams);\n}");
CompileShader(bytes, "vs"u8, "vs_5_0"u8, out var blob, "Point VS");
CompileShader(bytes, "ps"u8, "ps_5_0"u8, out var blob2, "Point 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 = 80u,
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 = "WORLD"u8)
{
fixed (byte* semanticName2 = "COLOR"u8)
{
fixed (byte* semanticName3 = "FADEPARAMS"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_R32G32B32A32_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;
}
}
}
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;
}
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);
ctx.Context->PSSetConstantBuffers(0u, 1u, &constantBuffer);
ctx.Context->GSSetShader(null, null, 0u);
}
public void Draw(FrameRenderContext ctx, Data data)
{
Bind(ctx);
data.DrawAll(ctx);
}
internal unsafe static void CompileShader(ReadOnlySpan<byte> source, ReadOnlySpan<byte> entryPoint, ReadOnlySpan<byte> target, out ID3DBlob* blob, string label)
{
ID3DBlob* ptr = null;
ID3DBlob* ptr2 = null;
HRESULT hRESULT;
fixed (byte* pSrcData = source)
{
fixed (byte* pEntrypoint = entryPoint)
{
fixed (byte* pTarget = target)
{
hRESULT = DirectX.D3DCompile(pSrcData, (nuint)source.Length, null, null, null, (sbyte*)pEntrypoint, (sbyte*)pTarget, 0u, 0u, &ptr, &ptr2);
}
}
}
string text = null;
if (ptr2 != null)
{
text = Encoding.UTF8.GetString((byte*)ptr2->GetBufferPointer(), (int)(nuint)ptr2->GetBufferSize()).TrimEnd('\0');
ptr2->Release();
}
AuspexService.Log.Debug(label + " compile: " + text);
if (hRESULT.FAILED)
{
if (ptr != null)
{
ptr->Release();
}
throw new InvalidOperationException("Shader compilation failed (" + label + "): " + text);
}
blob = ptr;
}
}