diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Context.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Context.cs
new file mode 100644
index 000000000..f6d4e1676
--- /dev/null
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Context.cs
@@ -0,0 +1,32 @@
+namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
+
+///
+/// Records rendering commands to be executed later by the .
+///
+///
+/// Threads have their own Context, accessed via .
+///
+[GenerateInterop]
+[StructLayout(LayoutKind.Explicit, Size = 0x2F78)]
+public unsafe partial struct Context {
+ [BitField(nameof(CurrentSubViewIndex), 28, 4)]
+ [FieldOffset(0x008)] private uint _flags;
+ [FieldOffset(0x00C)] public int ViewIndex;
+ [FieldOffset(0x010)] public void* CommandAllocationBase;
+
+ [FieldOffset(0x840)] public ulong CommandAllocationUsedSize;
+ [FieldOffset(0x848)] public ulong AllocationBase;
+ [FieldOffset(0x850)] public ulong AllocationUsedSize;
+
+ [MemberFunction("4C 8B D1 4C 8D 42 0F")]
+ public partial void* AllocateCommand(ulong size);
+
+ [MemberFunction("4C 8B C9 4D 8D 50 0F")]
+ public partial void* AllocateSpecificCommand(int commandType, ulong size);
+
+ [MemberFunction("E8 ?? ?? ?? ?? 8B 6E 6C")]
+ public partial void PushBackCommand(void* command);
+
+ [MemberFunction("E8 ?? ?? ?? ?? 8D 56 38")]
+ public partial void SetRenderTargets(int renderTargetCount, Texture** renderTargetTextures, Texture* depthStencilTexture, short a4, short a5, short a6, short a7); // last params believed to be a rectangle
+}
diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Device.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Device.cs
index 4bc6c6ea2..db69215d4 100644
--- a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Device.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Device.cs
@@ -1,3 +1,5 @@
+using FFXIVClientStructs.FFXIV.Common.Math;
+
namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
// Client::Graphics::Kernel::Device
@@ -10,7 +12,7 @@ public unsafe partial struct Device {
[StaticAddress("48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 7B 08 00", 3, isPointer: true)]
public static partial Device* Instance();
- [FieldOffset(0x8)] public void* ContextArray; // Client::Graphics::Kernel::Context array
+ [FieldOffset(0x8)] public void* ContextArray; // TODO: We have a struct for this now (breaking change)
[FieldOffset(0x10)] public void* RenderThread; // Client::Graphics::Kernel::RenderThread
[FieldOffset(0x28)] private CallbackManager* Unk28;
[FieldOffset(0x30)] private CallbackManager* Unk30;
@@ -111,8 +113,8 @@ public unsafe struct RenderCommandBufferGroup {
public unsafe partial struct RenderCommandSetTarget {
[FieldOffset(0x0)] public int SwitchType;
[FieldOffset(0x4)] public int RenderTargetCount;
- [FieldOffset(0x8), FixedSizeArray] internal FixedSizeArray4> _renderTargets;
- [FieldOffset(0x28)] public Texture* DepthBuffer;
+ [FieldOffset(0x8), FixedSizeArray] internal FixedSizeArray5> _renderTargets;
+ [FieldOffset(0x30)] public Texture* DepthBuffer;
[FieldOffset(0x38)] private float Unk0;
[FieldOffset(0x3C)] private float Unk1;
}
@@ -133,28 +135,33 @@ public unsafe partial struct RenderCommandViewport {
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
public unsafe partial struct RenderCommandScissorsRect {
[FieldOffset(0x0)] public int SwitchType;
- [FieldOffset(0x4)] public int Left;
- [FieldOffset(0x8)] public int Top;
- [FieldOffset(0xC)] public int Right;
- [FieldOffset(0x10)] public int Bottom;
+ [FieldOffset(0x4)] public Rectangle Rectangle;
+}
+
+public enum ClearFlags : uint {
+ None = 0,
+ Color = (1 << 0),
+ Depth = (1 << 1),
+ Stencil = (1 << 2),
}
[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public unsafe partial struct RenderCommandClearDepth {
[FieldOffset(0x0)] public int SwitchType;
- [FieldOffset(0x4)] public float ClearType;
+ [FieldOffset(0x4)] public ClearFlags ClearFlags;
[FieldOffset(0x8)] public float ColorB;
[FieldOffset(0xC)] public float ColorG;
[FieldOffset(0x10)] public float ColorR;
[FieldOffset(0x14)] public float ColorA;
[FieldOffset(0x18)] public float ClearDepth;
[FieldOffset(0x1C)] public int ClearStencil;
- [FieldOffset(0x20)] public int ClearCheck;
- [FieldOffset(0x24)] public float Top;
+ [CExporterTypeForce("D3D11_VIEWPORT*")]
+ [FieldOffset(0x20)] public void* ClearRectangle; // optional
[FieldOffset(0x28)] public float Left;
- [FieldOffset(0x2C)] public float Width;
- [FieldOffset(0x30)] public float Height;
- [FieldOffset(0x34)] public float MinZ;
- [FieldOffset(0x38)] public float MaxZ;
+ [FieldOffset(0x2C)] public float Top;
+ [FieldOffset(0x30)] public float Width;
+ [FieldOffset(0x34)] public float Height;
+ [FieldOffset(0x38)] public float MinZ;
+ [FieldOffset(0x3C)] public float MaxZ;
}
diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs
index 71836a4e9..5830b459c 100644
--- a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/ImmediateContext.cs
@@ -1,3 +1,5 @@
+using FFXIVClientStructs.FFXIV.Common.Math;
+
namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
// Client::Graphics::Kernel::ImmediateContext
@@ -8,10 +10,86 @@ namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
public unsafe partial struct ImmediateContext {
// Offset 0 is ID3D11DeviceContext
- //
- // Reset and assigned each rendered frame from Device->SwapChain->BackBuffer
- //
+ [FieldOffset(0x08)] public Rectangle CurrentScissorRect;
+
+ [Obsolete("Not necessarily the backbuffer, just the current primary render target. Prefer CurrentRenderTargets.")]
[FieldOffset(0x28)] public Texture* BackBufferReference;
+ ///
+ /// The currently bound render targets.
+ ///
+ [FieldOffset(0x28)] internal FixedSizeArray5> _currentRenderTargets;
+ ///
+ /// The currently bound depth stencil buffer, if any.
+ ///
+ [FieldOffset(0x50)] public Texture* CurrentDepthStencilBuffer;
+
+ [FieldOffset(0xAC)] public uint CurrentDepthState; // The 5 bits of depth state in PackedDepthStencilDesc with the stencil fields masked out
+ [FieldOffset(0xB0)] public PackedDepthStencilDesc CurrentStencilState; // With the depth fields masked out
+ // 0xC8: InputLayoutDesc
+
+ [FieldOffset(0xD0)] public VertexShader* CurrentVertexShader;
+ [FieldOffset(0x1B8)] public PixelShader* CurrentPixelShader;
+
+ // Certain types of complex clearing needs to be done by manually drawing to the render targets & depth buffer.
+ [FieldOffset(0x700)] public VertexShader* ManualClearQuadVertexShader;
+ [FieldOffset(0x708)] public PixelShader* ManualClearQuadPixelShader;
+ // 0x710: ManualClearQuad???
+ // 0x718: ManualClearQuadInputLayoutDesc
+ [FieldOffset(0x730)] public GeometryShader* CurrentGeometryShader;
+ [FieldOffset(0xB20)] public HullShader* CurrentHullShader;
+ [CExporterTypeForce("ID3D11DeviceContext*", true)]
[FieldOffset(0xBE8)] public void* D3D11DeviceContext;
+
+ [FieldOffset(0xF10)] public DomainShader* CurrentDomainShader;
+
+ [FieldOffset(0x17B3)] public byte BlendStateFlag; // Might be a bool to force creation of an underlying ID3D11BlendState*
+ [FieldOffset(0x17B4)] public PackedBlendStateDesc CurrentBlendState;
+ [CExporterTypeForce("ID3D11DeviceContext*", true)]
+ [FieldOffset(0x17B8)] public void* D3D11DeviceContext_2;
+
+ //[FieldOffset(0x1D70)] public InputLayout* CurrentInputLayout;
+ [CExporterTypeForce("D3D11_PRIMITIVE_TOPOLOGY", true)]
+ [FieldOffset(0x17D8)] public int CurrentPrimitiveTopology;
+
+ [MemberFunction("E8 ?? ?? ?? ?? 49 8D 47 58")]
+ public partial void SetBlendState(PackedBlendStateDesc blendState);
+
+ [MemberFunction("E8 ?? ?? ?? ?? 41 8B 56 1C 48 8B CE")]
+ public partial void SetDepthStencilState(byte obfuscatedDepthState, PackedDepthStencilDesc stencilState);
+
+ // Along with the given packed rasterizer state, the following constant values are also used:
+ // FrontCounterClockwise = true
+ // DepthClipEnable = true
+ // DepthBiasClamp = 100.0f
+ // AntialiasedLineEnable = false
+ [MemberFunction("E8 ?? ?? ?? ?? 49 8B 46 20 48 8B CE")]
+ public partial void SetRasterizerState(PackedRasterizerStateDesc rasterizerState);
+
+ [MemberFunction("E8 ?? ?? ?? ?? 4D 8B 46 68")]
+ public partial ulong SetVertexShader(VertexShader* vertexShader, void** constantBuffers);
+
+ [MemberFunction("E8 ?? ?? ?? ?? 4D 8B 46 78")]
+ public partial ulong SetPixelShader(PixelShader* pixelShader, void** constantBuffers);
+
+ [MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 8B 57 7C")]
+ public partial void SetViewport(Rectangle* viewportRectangle);
+
+ [MemberFunction("E9 ?? ?? ?? ?? 48 8B 4A 10")]
+ public partial void SetDefaultState();
+
+ [MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 83 7F ?? ?? 0F 84 ?? ?? ?? ?? 48 83 7F")]
+ public partial void DoClearCommandViaDraw(RenderCommandClearDepth* clearCommand);
+
+ [MemberFunction("48 89 6C 24 ?? 56 48 83 EC 40 48 83 7A")] // inlined in some places
+ public partial void DoClearCommand(RenderCommandClearDepth* clearCommand);
+
+ [MemberFunction("E8 ?? ?? ?? ?? 48 8B 7B 18 45 33 FF")]
+ public partial void ProcessCommands(RenderCommandBufferGroup* renderCommands, uint renderCommandCount);
+
+ [MemberFunction("E8 ?? ?? ?? ?? 48 8B 7B 18 45 33 FF")]
+ public partial void PreprocessCommands(RenderCommandBufferGroup* renderCommands, uint renderCommandCount);
+
+ [MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 0F B6 83 ?? ?? ?? ?? 3C 01 73 70")]
+ public partial RenderCommandBufferGroup* ExecuteCommands();
}
diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedBlendStateDesc.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedBlendStateDesc.cs
new file mode 100644
index 000000000..93cdacf71
--- /dev/null
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedBlendStateDesc.cs
@@ -0,0 +1,77 @@
+namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
+
+///
+/// Contains blending configuration for the renderer.
+///
+///
+/// Rather than manage a bunch of pointers to blend state objects, render commands accept a
+/// description of the desired blend state packed into 32 bits, which is copied around
+/// by value and eventually a real D3D blend state object is created within the
+/// as needed.
+///
+[GenerateInterop]
+[StructLayout(LayoutKind.Explicit, Size = 0x04)]
+public unsafe partial struct PackedBlendStateDesc {
+
+ // E (BlendEnabled)
+ // OOO (BlendOpMinusOne)
+ // SSSS (SrcBlendMinusOne)
+ // DDDD (DestBlendMinusOne)
+ // ooo (BlendOpAlphaMinusOne)
+ // sss s (SrcBlendAlphaMinusOne)
+ // ddd d (DestBlendAlphaMinusOne)
+ // MMM M (RenderTargetWriteMask)
+ // ---- - (--masked out--)
+ // xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
+ [BitField(nameof(BlendEnable), 0, 1)]
+ [BitField(nameof(BlendOpMinusOne), 1, 3)]
+ [BitField(nameof(SrcBlendMinusOne), 4, 4)]
+ [BitField(nameof(DestBlendMinusOne), 8, 4)]
+ [BitField(nameof(BlendOpAlphaMinusOne), 12, 3)]
+ [BitField(nameof(SrcBlendAlphaMinusOne), 15, 4)]
+ [BitField(nameof(DestBlendAlphaMinusOne), 19, 4)]
+ [BitField(nameof(RenderTargetWriteMask), 23, 4)]
+ [FieldOffset(0x00)] internal uint _value;
+
+ // D3D11_BLEND_OP
+ private partial byte BlendOpMinusOne { get; set; }
+ public byte BlendOp {
+ get => (byte)(BlendOpMinusOne + 1);
+ set => BlendOpMinusOne = (byte)(value - 1);
+ }
+
+ // D3D11_BLEND
+ private partial byte SrcBlendMinusOne { get; set; }
+ public byte SrcBlend {
+ get => (byte)(SrcBlendMinusOne + 1);
+ set => SrcBlendMinusOne = (byte)(value - 1);
+ }
+
+ // D3D11_BLEND
+ private partial byte DestBlendMinusOne { get; set; }
+ public byte DestBlend {
+ get => (byte)(DestBlendMinusOne + 1);
+ set => DestBlendMinusOne = (byte)(value - 1);
+ }
+
+ // D3D11_BLEND_OP
+ private partial byte BlendOpAlphaMinusOne { get; set; }
+ public byte BlendOpAlpha {
+ get => (byte)(BlendOpAlphaMinusOne + 1);
+ set => BlendOpAlphaMinusOne = (byte)(value - 1);
+ }
+
+ // D3D11_BLEND
+ private partial byte SrcBlendAlphaMinusOne { get; set; }
+ public byte SrcBlendAlpha {
+ get => (byte)(SrcBlendAlphaMinusOne + 1);
+ set => SrcBlendAlphaMinusOne = (byte)(value - 1);
+ }
+
+ // D3D11_BLEND
+ private partial byte DestBlendAlphaMinusOne { get; set; }
+ public byte DestBlendAlpha {
+ get => (byte)(DestBlendAlphaMinusOne + 1);
+ set => DestBlendAlphaMinusOne = (byte)(value - 1);
+ }
+}
diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedDepthStencilDesc.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedDepthStencilDesc.cs
new file mode 100644
index 000000000..b0e770ffb
--- /dev/null
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedDepthStencilDesc.cs
@@ -0,0 +1,109 @@
+namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
+
+///
+/// Contains depth stencil configuration for the renderer.
+///
+///
+/// Rather than manage a bunch of pointers to depth stencil objects, render commands accept a
+/// description of the desired depth stencil state packed into 64 bits, which is copied around
+/// by value and eventually a real D3D depth stencil object is created within the
+/// as needed.
+///
+[GenerateInterop]
+[StructLayout(LayoutKind.Explicit, Size = 0x08)]
+public unsafe partial struct PackedDepthStencilDesc {
+
+ // D (DepthEnable)
+ // d (DepthWriteMask) D3D11_DEPTH_WRITE_MASK
+ // f ff (DepthFuncMinusOne)
+ // - ---- --- (--masked out--)
+ // SSS (BackFaceStencilFuncMinusOne)
+ // fff (BackFaceStencilFailOpMinusOne)
+ // DD D (BackFaceStencilDepthFailOpMinusOne)
+ // P PP (BackFaceStencilPassOpMinusOne)
+ // S (StencilEnable)
+ // s ss (FrontFaceStencilFuncMinusOne)
+ // FFF (FrontFaceStencilFailOpMinusOne)
+ // xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
+ [BitField(nameof(DepthEnable), 0, 1)]
+ [BitField(nameof(DepthWriteMask), 1, 1)]
+ [BitField(nameof(DepthFuncMinusOne), 2, 3)]
+ [BitField(nameof(BackFaceStencilFuncMinusOne), 13, 3)]
+ [BitField(nameof(BackFaceStencilFailOpMinusOne), 16, 3)]
+ [BitField(nameof(BackFaceStencilDepthFailOpMinusOne), 19, 3)]
+ [BitField(nameof(BackFaceStencilPassOpMinusOne), 22, 3)]
+ [BitField(nameof(StencilEnable), 25, 1)]
+ [BitField(nameof(FrontFaceStencilFuncMinusOne), 26, 3)]
+ [BitField(nameof(FrontFaceStencilFailOpMinusOne), 29, 3)]
+ [FieldOffset(0x00)] internal uint _firstPart;
+
+ // - (--masked out--)
+ // D (BackFaceStencilIndependent)
+ // d dd (FrontFaceStencilDepthFailOpMinusOne)
+ // ppp (FrontFaceStencilPassOpMinusOne)
+ // ???? ???? (--often masked out, unsure--)
+ // RRRR RRRR (StencilReadMask)
+ // WWWW WWWW (StencilWriteMask)
+ // xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
+ [BitField(nameof(BackFaceStencilIndependent), 1, 1)]
+ [BitField(nameof(FrontFaceStencilDepthFailOpMinusOne), 2, 3)]
+ [BitField(nameof(FrontFaceStencilPassOpMinusOne), 5, 3)]
+ [FieldOffset(0x04)] internal byte _secondPart;
+
+ [FieldOffset(0x06)] public byte StencilReadMask;
+ [FieldOffset(0x07)] public byte StencilWriteMask;
+
+ private partial byte DepthFuncMinusOne { get; set; }
+ public byte DepthFunc {
+ get => (byte)(DepthFuncMinusOne + 1);
+ set => DepthFuncMinusOne = (byte)(value - 1);
+ }
+
+ private partial byte BackFaceStencilFuncMinusOne { get; set; }
+ public byte BackFaceStencilFunc {
+ get => (byte)(BackFaceStencilFuncMinusOne + 1);
+ set => BackFaceStencilFuncMinusOne = (byte)(value - 1);
+ }
+
+ private partial byte BackFaceStencilFailOpMinusOne { get; set; }
+ public byte BackFaceStencilFailOp {
+ get => (byte)(BackFaceStencilFailOpMinusOne + 1);
+ set => BackFaceStencilFailOpMinusOne = (byte)(value - 1);
+ }
+
+ private partial byte BackFaceStencilDepthFailOpMinusOne { get; set; }
+ public byte BackFaceStencilDepthFailOp {
+ get => (byte)(BackFaceStencilDepthFailOpMinusOne + 1);
+ set => BackFaceStencilDepthFailOpMinusOne = (byte)(value - 1);
+ }
+
+ private partial byte BackFaceStencilPassOpMinusOne { get; set; }
+ public byte BackFaceStencilPassOp {
+ get => (byte)(BackFaceStencilPassOpMinusOne + 1);
+ set => BackFaceStencilPassOpMinusOne = (byte)(value + 1);
+ }
+
+ private partial byte FrontFaceStencilFuncMinusOne { get; set; }
+ public byte FrontFaceStencilFunc {
+ get => (byte)(FrontFaceStencilFuncMinusOne + 1);
+ set => FrontFaceStencilFuncMinusOne = (byte)(value - 1);
+ }
+
+ private partial byte FrontFaceStencilFailOpMinusOne { get; set; }
+ public byte FrontFaceStencilFailOp {
+ get => (byte)(FrontFaceStencilFailOpMinusOne + 1);
+ set => FrontFaceStencilFailOpMinusOne = (byte)(value - 1);
+ }
+
+ private partial byte FrontFaceStencilDepthFailOpMinusOne { get; set; }
+ public byte FrontFaceStencilDepthFailOp {
+ get => (byte)(FrontFaceStencilDepthFailOpMinusOne + 1);
+ set => FrontFaceStencilDepthFailOpMinusOne = (byte)(value - 1);
+ }
+
+ private partial byte FrontFaceStencilPassOpMinusOne { get; set; }
+ public byte FrontFaceStencilPassOp {
+ get => (byte)(FrontFaceStencilPassOpMinusOne + 1);
+ set => FrontFaceStencilPassOpMinusOne = (byte)(value + 1);
+ }
+}
diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedRasterizerState.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedRasterizerState.cs
new file mode 100644
index 000000000..99aee9c27
--- /dev/null
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/PackedRasterizerState.cs
@@ -0,0 +1,66 @@
+namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
+
+///
+/// Contains rasterization configuration for the renderer.
+///
+///
+/// Rather than manage a bunch of pointers to rasterizer state objects, render commands accept a
+/// description of the desired rasterizer state packed into 32 bits, which is copied around
+/// by value and eventually a real D3D rasterizer state object is created within the
+/// as needed.
+///
+[GenerateInterop]
+[StructLayout(LayoutKind.Explicit, Size = 0x04)]
+public unsafe partial struct PackedRasterizerStateDesc {
+ private const float _depthBiasScale = 0.0000152587890625f; // 2 ^ -16
+ private const float _slopeScaledDepthBiasQuantization = 0.03125f;
+
+ // FF (FillMode)
+ // c (HasCullMode)
+ // C (CullModeBit)
+ // S (ScissorEnable)
+ // B BBBB BBBB BBBB BBB (DepthBiasHalf) (float16 * 2e-16)
+ // LLL LLLL LLL (SlopeScaledDepthBiasQuantized) (uint10 / 0.03125f)
+ // - (--masked out--)
+ // xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
+ [BitField(nameof(FillMode), 0, 2)]
+ [BitField(nameof(HasCullMode), 2, 1)]
+ [BitField(nameof(CullModeBit), 3, 1)]
+ [BitField(nameof(ScissorEnable), 4, 1)]
+ [BitField(nameof(DepthBiasHalf), 5, 16)]
+ [BitField(nameof(SlopeScaledDepthBiasQuantized), 21, 10)]
+ [FieldOffset(0x00)] internal uint _value;
+
+ private partial bool HasCullMode { get; set; }
+ private partial byte CullModeBit { get; set; }
+ public byte CullMode {
+ get {
+ if (HasCullMode) {
+ return (byte)(3 - CullModeBit);
+ } else {
+ return 1; // D3D11_CULL_NONE
+ }
+ }
+ set {
+ if (value == 1) { // D3D11_CULL_NONE
+ HasCullMode = false;
+ CullModeBit = 0;
+ } else {
+ HasCullMode = true;
+ CullModeBit = (byte)((3 - value) & 1);
+ }
+ }
+ }
+
+ private partial ushort DepthBiasHalf { get; set; }
+ public float DepthBias {
+ get => (float)BitConverter.UInt16BitsToHalf(DepthBiasHalf) / _depthBiasScale;
+ set => DepthBiasHalf = BitConverter.HalfToUInt16Bits((Half)(value * _depthBiasScale));
+ }
+
+ private partial ushort SlopeScaledDepthBiasQuantized { get; set; }
+ public float SlopeScaledDepthBias {
+ get => SlopeScaledDepthBiasQuantized * _slopeScaledDepthBiasQuantization;
+ set => SlopeScaledDepthBiasQuantized = (ushort)(value / _slopeScaledDepthBiasQuantization);
+ }
+}
diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Texture.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Texture.cs
index 8b5c02b68..415f0941f 100644
--- a/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Texture.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Texture.cs
@@ -29,6 +29,14 @@ public unsafe partial struct Texture {
[FieldOffset(0x68)] public void* D3D11Texture2D; // ID3D11Texture2D1
[FieldOffset(0x70)] public void* D3D11ShaderResourceView; // ID3D11ShaderResourceView1
+ // Each mip of each array element needs its own render target
+ [FieldOffset(0x80)] public TextureMipRenderTarget* MipRenderTargets;
+
+ public TextureMipRenderTarget* GetMipRenderTarget(int mipLevel, int arrayElementIndex = 0) {
+ // Each mip is stored contiguously. So all elements of the first mip, then the second, etc.
+ return &MipRenderTargets[mipLevel * ArraySize + arrayElementIndex];
+ }
+
public static Texture* CreateTexture2D(int width, int height, byte mipLevel, TextureFormat textureFormat, TextureFlags flags, uint unk) {
var size = stackalloc int[2];
size[0] = width;
@@ -49,6 +57,11 @@ public unsafe partial struct Texture {
public partial void DecRef();
}
+[StructLayout(LayoutKind.Explicit, Size = 0x40)]
+public unsafe struct TextureMipRenderTarget {
+ [FieldOffset(0x00)] public void* D3D11RenderTargetViewOrDepthStencilView; // ID3D11RenderTargetView(1?) or ID3D11DepthStencilView(1?)
+}
+
// See also DXGI_FORMATs that end with the same names.
public enum TextureFormat : uint {
L8_UNORM = 0x1130,
diff --git a/FFXIVClientStructs/FFXIV/Client/Graphics/Render/RenderTargetManager.cs b/FFXIVClientStructs/FFXIV/Client/Graphics/Render/RenderTargetManager.cs
index 51e424042..d79db7ba5 100644
--- a/FFXIVClientStructs/FFXIV/Client/Graphics/Render/RenderTargetManager.cs
+++ b/FFXIVClientStructs/FFXIV/Client/Graphics/Render/RenderTargetManager.cs
@@ -128,8 +128,8 @@ public unsafe partial struct RenderTargetManager {
[FieldOffset(0x558)] internal Texture* Unk558;
[FieldOffset(0x560)] internal Texture* Unk560;
[FieldOffset(0x568)] internal Texture* Unk568;
- [FieldOffset(0x570)] internal Texture* Unk570;
- [FieldOffset(0x578)] internal Texture* Unk578; // apparently BackBuffer, see 48 89 87 ?? ?? ?? ?? 48 8B 47 ?? 48 89 87 ?? ?? ?? ?? 8B 06
+ [FieldOffset(0x570)] public Texture* Unk570; // apparently BackBuffer, see 48 89 87 ?? ?? ?? ?? 48 8B 47 ?? 48 89 87 ?? ?? ?? ?? 8B 06
+ [FieldOffset(0x578)] public Texture* Unk578; // apparently DepthStencil
[FieldOffset(0x580)] internal Texture* Unk580;
[FieldOffset(0x588)] internal Texture* Unk588;
[FieldOffset(0x590)] internal Texture* Unk590;
diff --git a/FFXIVClientStructs/FFXIV/Client/UI/UIModule.cs b/FFXIVClientStructs/FFXIV/Client/UI/UIModule.cs
index 83afe75f5..b8fa88f40 100644
--- a/FFXIVClientStructs/FFXIV/Client/UI/UIModule.cs
+++ b/FFXIVClientStructs/FFXIV/Client/UI/UIModule.cs
@@ -125,6 +125,9 @@ public unsafe partial struct UIModule {
[MemberFunction("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F2 48 8B F9 45 84 C9")]
public partial void ProcessChatBoxEntry(Utf8String* message, nint a4 = 0, bool saveToHistory = false);
+ [MemberFunction("48 83 EC ?? ?? ?? ?? FF 50 ?? 48 8B C8 48 83 C4 ?? E9 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 40 53 48 83 EC ?? ?? ?? ?? 48 8B D9")]
+ public partial long Draw2D();
+
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public struct UIModuleHandler {
// called via AtkModuleEvent.CallHandler()
diff --git a/FFXIVClientStructs/FFXIV/Component/GUI/AtkServer.cs b/FFXIVClientStructs/FFXIV/Component/GUI/AtkServer.cs
new file mode 100644
index 000000000..d2d01c47b
--- /dev/null
+++ b/FFXIVClientStructs/FFXIV/Component/GUI/AtkServer.cs
@@ -0,0 +1,68 @@
+using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
+using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
+
+namespace FFXIVClientStructs.FFXIV.Component.GUI;
+
+///
+/// Handles UI rendering by taking UI drawing commands and submitting them as kernel drawing commands.
+///
+[GenerateInterop]
+[StructLayout(LayoutKind.Explicit, Size = 0x8680)]
+public unsafe partial struct AtkServer {
+ [FieldOffset(0x10)] public ShaderCodeResourceHandle* PrimitiveUIVSResource;
+ [FieldOffset(0x18)] internal ShaderCodeResourceHandle* _primitiveUIVSResource_2;
+ [FieldOffset(0x20)] internal ShaderCodeResourceHandle* _primitiveUIVSResource_3;
+ [FieldOffset(0x28)] internal ShaderCodeResourceHandle* _primitiveUIVSResource_4;
+ [FieldOffset(0x30)] public ShaderCodeResourceHandle* FontVSResource;
+ [FieldOffset(0x38)] public ShaderCodeResourceHandle* FontEdgeVSResource;
+ [FieldOffset(0x40)] public ShaderCodeResourceHandle* FontGlareVSResource;
+ [FieldOffset(0x48)] public ShaderCodeResourceHandle* FontHighlightVSResource;
+ [FieldOffset(0x50)] public ShaderCodeResourceHandle* FontEmbossVSResource;
+ [FieldOffset(0x58)] public ShaderCodeResourceHandle* PrimitiveUIPSResource;
+ [FieldOffset(0x60)] public ShaderCodeResourceHandle* PrimitiveUINaviMapPSResource;
+ [FieldOffset(0x68)] public ShaderCodeResourceHandle* PrimitiveUResourceIAreaMapPSResource;
+ [FieldOffset(0x70)] public ShaderCodeResourceHandle* PrimitiveUICharaViewPSResource;
+ [FieldOffset(0x78)] public ShaderCodeResourceHandle* FontPSResource;
+ [FieldOffset(0x80)] public ShaderCodeResourceHandle* FontEdgePSResource;
+ [FieldOffset(0x88)] public ShaderCodeResourceHandle* FontGlarePSResource;
+ [FieldOffset(0x90)] public ShaderCodeResourceHandle* FontHightlightPSResource;
+ [FieldOffset(0x98)] public ShaderCodeResourceHandle* FontEmbossPSResource;
+ [FieldOffset(0xA0)] public ShaderCodeResourceHandle* PrimitiveUIMaskPSResource;
+
+
+ [FieldOffset(0xE8)] public VertexShader* PrimitiveUIVS;
+ [FieldOffset(0xF0)] internal VertexShader* _primitiveUIVS_2;
+ [FieldOffset(0xF8)] internal VertexShader* _primitiveUIVS_3;
+ [FieldOffset(0x100)] internal VertexShader* _primitiveUIVS_4;
+ [FieldOffset(0x108)] public VertexShader* FontVS;
+ [FieldOffset(0x110)] public VertexShader* FontEdgeVS;
+ [FieldOffset(0x118)] public VertexShader* FontGlareVS;
+ [FieldOffset(0x120)] public VertexShader* FontHighlightVS;
+ [FieldOffset(0x128)] public VertexShader* FontEmbossVS;
+ [FieldOffset(0x130)] public PixelShader* PrimitiveUIPS;
+ [FieldOffset(0x138)] public PixelShader* PrimitiveUINaviMapPS;
+ [FieldOffset(0x140)] public PixelShader* PrimitiveUIAreaMapPS;
+ [FieldOffset(0x148)] public PixelShader* PrimitiveUICharaViewPS;
+ [FieldOffset(0x150)] public PixelShader* FontPS;
+ [FieldOffset(0x158)] public PixelShader* FontEdgePS;
+ [FieldOffset(0x160)] public PixelShader* FontGlarePS;
+ [FieldOffset(0x168)] public PixelShader* FontHightlightPS;
+ [FieldOffset(0x170)] public PixelShader* FontEmbossPS;
+ [FieldOffset(0x178)] public PixelShader* PrimitiveUIMaskPS;
+
+
+ [FieldOffset(0x4C0)] public Texture* WhiteTexture; // 4x4 solid white
+ [FieldOffset(0x4C8)] public Texture* BlackTexture; // 4x4 solid black
+
+ [VirtualFunction(0)]
+ public partial AtkServer* Dtor(byte flags);
+
+ [MemberFunction("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 50 44 8B 05")]
+ public partial ulong Draw(bool unk);
+
+ [MemberFunction("E9 ?? ?? ?? ?? CC CC CC CC CC CC CC CC CC CC 48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 7C 24 ?? 41 56")]
+ public partial ulong ProcessUICommands(bool unk);
+
+ [MemberFunction("E8 ?? ?? ?? ?? EB 05 E8 ?? ?? ?? ?? 4C 8D 5C 24")]
+ public partial ulong ProcessUICommandsAlt(bool unk);
+}
diff --git a/FFXIVClientStructs/Interop/ThreadLocals.cs b/FFXIVClientStructs/Interop/ThreadLocals.cs
new file mode 100644
index 000000000..06be3f187
--- /dev/null
+++ b/FFXIVClientStructs/Interop/ThreadLocals.cs
@@ -0,0 +1,25 @@
+using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
+
+namespace FFXIVClientStructs.Interop;
+
+[GenerateInterop]
+[StructLayout(LayoutKind.Explicit, Size = 0xE0AC8)]
+public unsafe partial struct ThreadLocals {
+
+ [FieldOffset(0x238)] public Context* GraphicsKernelContext;
+
+
+ // Not in data.yml as this is already named by IDA and isn't technically a class member
+ [MemberFunction("65 48 8B 04 25 30 00 00 00 C3")]
+ public static partial _TEB* NtCurrentTeb();
+
+ [CExporterIgnore]
+ [StructLayout(LayoutKind.Explicit, Size = 0x100)] // undefined size, see https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
+ public struct _TEB {
+ [FieldOffset(0x58)] public ThreadLocals** ThreadLocalStoragePointer;
+ }
+
+ public static ThreadLocals* ThreadLocalInstance() {
+ return *NtCurrentTeb()->ThreadLocalStoragePointer;
+ }
+}
diff --git a/ida/data.yml b/ida/data.yml
index 5fff59109..50b429b0b 100644
--- a/ida/data.yml
+++ b/ida/data.yml
@@ -133,6 +133,7 @@ functions:
0x141D692FC: std::_Xlength_error
0x1401E7740: std::deque::_Growmap
0x1406E22A0: IntToString
+ 0x1402406C0: HalfToFloat
0x14005F250: GetStdOut
0x14005F260: vsprintf_s
0x14005F2C0: vswprintf_s
@@ -6731,6 +6732,9 @@ classes:
Client::Graphics::Kernel::Context:
funcs:
0x140240750: PushBackCommand
+ 0x14023FB20: AllocateCommand
+ 0x1402EEAA0: AllocateSpecificCommand
+ 0x14023BBC0: SetRenderTargets
#oldfail 0x140377E90: PrepareModel
#oldfail 0x1403DEC50: Draw_cmd
Client::Graphics::Kernel::Device::ImmediateContextBase:
@@ -6742,6 +6746,17 @@ classes:
base: Client::Graphics::Kernel::Device::ImmediateContextBase
funcs:
0x140234380: ProcessCommands
+ 0x140231AA0: PreprocessCommands
+ 0x1402345F0: ExecuteCommands
+ 0x140240AA0: SetBlendState
+ 0x140240E20: SetDepthStencilState
+ 0x140241A00: SetRasterizerState
+ 0x140241AA0: SetVertexShader
+ 0x1402417C0: SetPixelShader
+ 0x14022B770: SetViewport
+ 0x14022D250: SetDefaultState
+ 0x14022ACE0: DoClearCommandViaDraw
+ 0x14022ABB0: DoClearCommand
#oldfail 0x14036D6C0: PrimeForDraw
Client::Graphics::Kernel::Device::RenderThread:
vtbls:
@@ -7914,8 +7929,13 @@ classes:
Component::GUI::AtkServer:
vtbls:
- ea: 0x142171BE8
+ vfuncs:
+ 1: Dtor
funcs:
0x140639BE0: ctor
+ 0x14063B250: Draw
+ 0x1406E5900: ProcessUICommands
+ 0x1406E64A0: ProcessUICommandsAlt
Component::GUI::AtkEventTarget:
vtbls:
- ea: 0x142171C78
@@ -9704,6 +9724,7 @@ classes:
0x140787A90: Update
0x140787F00: HandleInputUpdate
0x14078D100: ProcessChatBoxEntry
+ 0x140787E10: Draw2D
0x14078DEC0: PropagateLevelChange
0x14078DF10: PropagateClassJobChange
0x1407861F0: ScreenShotCallback