DP-CustomResolution/CustomResolution2782/MiniShader.cs

184 lines
4.9 KiB
C#

using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Common.Lua;
using MonoMod.Utils;
using Newtonsoft.Json;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Linq;
using TerraFX.Interop.DirectX;
using SDXDevice = SharpDX.Direct3D11.Device;
using XIVDevice = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device;
namespace CustomResolution;
public unsafe class MiniShader : IDisposable
{
public MiniShader(string name)
{
Name = name;
var asm = Assembly.GetExecutingAssembly();
var path = $"{nameof(CustomResolution)}.Shaders.{name}.hlsl";
using (var stream = asm.GetManifestResourceStream(path) ?? throw new FileNotFoundException(path))
using (var streamReader = new StreamReader(stream))
{
Source = streamReader.ReadToEnd();
}
var dev = (DeviceEx*) XIVDevice.Instance();
Ctx = CppObject.FromPointer<DeviceContext>((nint) dev->D3D11DeviceContext);
Device = Ctx.Device;
VS = new(Device, ShaderBytecode.Compile(Source, "vs", "vs_5_0"));
PS = new(Device, ShaderBytecode.Compile(Source, "ps", "ps_5_0"));
}
public string Name { get; private set; }
public string Source { get; private set; }
public DeviceContext Ctx { get; private set; }
public SDXDevice Device { get; private set; }
public SharpDX.Direct3D11.VertexShader VS { get; private set; }
public SharpDX.Direct3D11.PixelShader PS { get; private set; }
public virtual void Dispose()
{
PS.Dispose();
VS.Dispose();
Device.Dispose();
}
}
public class MiniShaderConstants<T> : IDisposable where T : unmanaged
{
private readonly MiniShader _shader;
private T _value;
public MiniShaderConstants(MiniShader shader)
{
_shader = shader;
Buffer = new(
_shader.Device,
Math.Max(16, Marshal.SizeOf<T>()),
ResourceUsage.Default,
BindFlags.ConstantBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
Marshal.SizeOf<T>()
);
}
public SharpDX.Direct3D11.Buffer Buffer { get; private set; }
public ref T Value => ref _value;
public void Dispose()
{
Buffer.Dispose();
}
public void Update()
{
Set(Value);
}
public void Set(T data)
{
_value = data;
_shader.Ctx.UpdateSubresource(ref data, Buffer);
}
}
public class MiniShaderState : IDisposable
{
private List<Action> _undos = [];
public MiniShaderState()
{
}
public void Dispose()
{
foreach (var undo in _undos)
{
undo();
}
}
public void Set<TOwner, TValue>(TOwner owner, string name, TValue value)
{
var prev = ReflCache<TOwner>.Get<TValue>(owner, name);
_undos.Add(() => ReflCache<TOwner>.Set(owner, name, prev));
ReflCache<TOwner>.Set(owner, name, value);
}
public void SetShader<T>(CommonShaderStage<T> stage, T? shader) where T : DeviceChild
{
var prev = stage.Get();
_undos.Add(() => stage.Set(prev));
stage.Set(shader!);
}
public void SetConstantBuffer<T>(CommonShaderStage<T> stage, int slot, SharpDX.Direct3D11.Buffer buffer) where T : DeviceChild
{
var prev = stage.GetConstantBuffers(slot, 1);
_undos.Add(() => stage.SetConstantBuffer(slot, prev[0]));
stage.SetConstantBuffer(slot, buffer);
}
public void SetShaderResource<T>(CommonShaderStage<T> stage, int slot, ShaderResourceView srv) where T : DeviceChild
{
var prev = stage.GetShaderResources(slot, 1);
_undos.Add(() => stage.SetShaderResource(slot, prev[0]));
stage.SetShaderResource(slot, srv);
}
private static class ReflCache<TOwner>
{
private static readonly Type _tOwner = typeof(TOwner);
private static readonly Dictionary<string, PropertyInfo?> _props = [];
private static PropertyInfo? GetProperty(string name)
{
if (_props.TryGetValue(name, out var prop))
{
return prop;
}
return _props[name] = _tOwner.GetProperty(name);
}
public static TValue Get<TValue>(TOwner owner, string name)
{
if (GetProperty(name) is { } prop)
{
return (TValue) prop.GetValue(owner)!;
}
return default!;
}
public static void Set<TValue>(TOwner owner, string name, TValue value)
{
if (GetProperty(name) is { } prop)
{
prop.SetValue(owner, value);
return;
}
}
}
}