Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
81aeda6
Adds client graphics kernel context.
universalconquistador May 14, 2026
e3c9ef1
Adds ImmediateContext stuff and packed render state structs.
universalconquistador May 14, 2026
b7f02b8
Adds render target view / depth stencil view access to Textures.
universalconquistador May 14, 2026
0f2118f
Adds UI drawing stuff.
universalconquistador May 14, 2026
67e25b0
Removes address comment I left in by accident
universalconquistador May 14, 2026
9954873
Fixes misnamed ImmediateContext field.
universalconquistador May 14, 2026
73525a5
Improves clear rectangle field name.
universalconquistador May 14, 2026
ab37087
Removes another address comment I left in by mistake.
universalconquistador May 14, 2026
265c466
Adds missing force bool to DirectX CExporter type override.
universalconquistador May 14, 2026
62e6622
Restores some backwards-compatibility with improved fields.
universalconquistador May 14, 2026
0f2f956
Corrects render command rectangles and re-adds obsoleted fields.
universalconquistador May 20, 2026
321e11e
Adds packeddepthstencil stencil ref field.
universalconquistador May 20, 2026
1bd8e90
Fixes void return type of uimodule draw2d.
universalconquistador May 20, 2026
85331c8
Gives public unk's actual names.
universalconquistador May 20, 2026
f90eec1
Improves comments
universalconquistador May 22, 2026
c6b57a9
Opinionated style changes
Haselnussbomber May 27, 2026
2c5b0f2
Add GenerateInterop to RenderCommandMultiViewport
Haselnussbomber May 27, 2026
325e3c5
Fix some return types
Haselnussbomber May 27, 2026
3dac72c
Add AtkServer pointer to AtkModule
Haselnussbomber May 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Context.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;

/// <summary>
/// Records rendering commands to be executed later by the <see cref="ImmediateContext"/>.
/// </summary>
/// <remarks>
/// Threads have their own Context, accessed via <see cref="ThreadLocals.GraphicsKernelContext"/>.
/// </remarks>
[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x2F78)]
public unsafe partial struct Context {
[BitField<byte>(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
}
100 changes: 69 additions & 31 deletions FFXIVClientStructs/FFXIV/Client/Graphics/Kernel/Device.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using FFXIVClientStructs.FFXIV.Common.Math;

namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;

// Client::Graphics::Kernel::Device
Expand All @@ -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)
Copy link
Copy Markdown
Collaborator

@Haselnussbomber Haselnussbomber May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as a note, I would have no idea what to change this to. I guess Context* something?
Would rather add a Span for that if the size is constant and the length is known.

[FieldOffset(0x10)] public void* RenderThread; // Client::Graphics::Kernel::RenderThread
[FieldOffset(0x28)] private CallbackManager* Unk28;
[FieldOffset(0x30)] private CallbackManager* Unk30;
Expand Down Expand Up @@ -71,7 +73,7 @@ public unsafe partial struct Device {
// Client::Graphics::Kernel::Device::CallbackManager
[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public unsafe partial struct CallbackManager {
public partial struct CallbackManager {
[FieldOffset(0x8)] public void* Lock; // CRITICAL_SECTION

[FieldOffset(0x30)] public Entry* Entries;
Expand All @@ -89,7 +91,7 @@ public unsafe partial struct CallbackManager {

// Unsure about the names of things inside CallbackManager though
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public unsafe partial struct Entry {
public struct Entry {
[FieldOffset(0x0)] public void* Function;
[FieldOffset(0x8)] public void* Context;
}
Expand All @@ -106,55 +108,91 @@ public unsafe struct RenderCommandBufferGroup {
[FieldOffset(0x8), CExporterUnion("RenderCommand")] public RenderCommandClearDepth* ClearDepthCommand;
}

public enum RenderCommandType : int {
SetTarget = 0,
Viewport = 1,
MultiViewport = 2,
ScissorRect = 3,
Clear = 4,
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
public unsafe partial struct RenderCommandSetTarget {
[FieldOffset(0x0)] public int SwitchType;
[FieldOffset(0x0)] public RenderCommandType Type;
[FieldOffset(0x4)] public int RenderTargetCount;
[FieldOffset(0x8), FixedSizeArray] internal FixedSizeArray4<Pointer<Texture>> _renderTargets;
[FieldOffset(0x28)] public Texture* DepthBuffer;
[FieldOffset(0x8), FixedSizeArray] internal FixedSizeArray5<Pointer<Texture>> _renderTargets;
[FieldOffset(0x30)] public Texture* DepthBuffer;
[FieldOffset(0x38)] private float Unk0;
[FieldOffset(0x3C)] private float Unk1;

[FieldOffset(0x0), Obsolete("Use Type instead.")] public int SwitchType;
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
public unsafe partial struct RenderCommandViewport {
[FieldOffset(0x0)] public int SwitchType;
[FieldOffset(0x04)] public int TopLeftY;
[FieldOffset(0x08)] public int TopLeftX;
[FieldOffset(0x0C)] public int BottomRightY;
[FieldOffset(0x10)] public int BottomRightX;
public struct RenderCommandViewport {
[FieldOffset(0x0)] public RenderCommandType Type;
[FieldOffset(0x4)] public IntRectangle ViewportRect;
[FieldOffset(0x14)] public float MinDepth;
[FieldOffset(0x18)] public float MaxDepth;

[FieldOffset(0x0), Obsolete("Use Type instead.")] public int SwitchType;
[FieldOffset(0x04), Obsolete("Use ViewportRect.Left.")] public int TopLeftY;
[FieldOffset(0x08), Obsolete("Use ViewportRect.Top.")] public int TopLeftX;
[FieldOffset(0x0C), Obsolete("Use ViewportRect.Right.")] public int BottomRightY;
[FieldOffset(0x10), Obsolete("Use ViewportRect.Bottom.")] public int BottomRightX;
}

[GenerateInterop]
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
public unsafe partial struct RenderCommandScissorsRect {
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
public partial struct RenderCommandMultiViewport {
[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), FixedSizeArray] internal FixedSizeArray5<IntRectangle> _viewportRects;
[FieldOffset(0x54), FixedSizeArray] internal FixedSizeArray5<float> _minDepths;
[FieldOffset(0x68), FixedSizeArray] internal FixedSizeArray5<float> _maxDepths;
[FieldOffset(0x7C)] public uint ViewportCount;
}

[StructLayout(LayoutKind.Explicit, Size = 0x20)]
public struct RenderCommandScissorsRect {
[FieldOffset(0x0)] public RenderCommandType Type;
[FieldOffset(0x4)] public IntRectangle ScissorRect;

[FieldOffset(0x0), Obsolete("Use Type instead.")] public int SwitchType;
[FieldOffset(0x4), Obsolete("Use ScissorRect.Left.")] public int Left;
[FieldOffset(0x8), Obsolete("Use ScissorRect.Top.")] public int Top;
[FieldOffset(0xC), Obsolete("Use ScissorRect.Right.")] public int Right;
[FieldOffset(0x10), Obsolete("Use ScissorRect.Bottom.")] public int Bottom;
}

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;
public unsafe struct RenderCommandClearDepth {
[FieldOffset(0x0)] public RenderCommandType Type;
[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;
[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(0x1C)] public byte ClearStencil;
[FieldOffset(0x1D)] public byte StencilReference;
[FieldOffset(0x20), CExporterTypeForce("D3D11_RECT*")] public IntRectangle* ClearRectanglePtr; // optional, generally points at ClearRectangle if set
[FieldOffset(0x28)] public IntRectangle ClearRectangle;
[FieldOffset(0x38)] public float MinZ;
[FieldOffset(0x3C)] public float MaxZ;

[FieldOffset(0x0), Obsolete("Use Type instead.")] public int SwitchType;
[FieldOffset(0x4), Obsolete("This is incorrect. Use ClearFlags.")] public float ClearType;
[FieldOffset(0x20), Obsolete("This is incorrect. Use ClearRectanglePtr.")] public int ClearCheck;
[FieldOffset(0x28), Obsolete("This is incorrect. Use ClearRectangle.Left instead.")] public float Left;
[FieldOffset(0x2C), Obsolete("This is incorrect. Use ClearRectangle.Top instead.")] public float Top;
[FieldOffset(0x30), Obsolete("This is incorrect. Use ClearRectangle.Right instead.")] public float Width;
[FieldOffset(0x34), Obsolete("This is incorrect. Use ClearRectangle.Bottom instead.")] public float Height;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using FFXIVClientStructs.FFXIV.Common.Math;

namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;

// Client::Graphics::Kernel::ImmediateContext
Expand All @@ -8,10 +10,86 @@ namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
public unsafe partial struct ImmediateContext {
// Offset 0 is ID3D11DeviceContext

// <remark>
// Reset and assigned each rendered frame from Device->SwapChain->BackBuffer
// </remark>
[FieldOffset(0x08)] public Rectangle CurrentScissorRect;

[Obsolete("Not necessarily the backbuffer, just the current primary render target. Prefer CurrentRenderTargets.")]
[FieldOffset(0x28)] public Texture* BackBufferReference;
/// <summary>
/// The currently bound render targets.
/// </summary>
[FieldOffset(0x28)] internal FixedSizeArray5<Pointer<Texture>> _currentRenderTargets;
/// <summary>
/// The currently bound depth stencil buffer, if any.
/// </summary>
[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;

[FieldOffset(0xBE8), CExporterTypeForce("ID3D11DeviceContext*", true)] 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;
[FieldOffset(0x17B8), CExporterTypeForce("ID3D11DeviceContext*", true)] public void* D3D11DeviceContext_2;

//[FieldOffset(0x1D70)] public InputLayout* CurrentInputLayout;
[FieldOffset(0x17D8), CExporterTypeForce("D3D11_PRIMITIVE_TOPOLOGY", true)] 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 void SetVertexShader(VertexShader* vertexShader, void** constantBuffers);

[MemberFunction("E8 ?? ?? ?? ?? 4D 8B 46 78")]
public partial void SetPixelShader(PixelShader* pixelShader, void** constantBuffers);

[MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 8B 57 7C")]
public partial void SetViewport(IntRectangle* viewportRectangle, float minDepth, float maxDepth);

[MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B D7 48 8B CB E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 83 7F")]
public partial void SetMultiViewports(uint viewportCount, IntRectangle* viewports, float* minDepths, float* maxDepths);

[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);

[FieldOffset(0xBE8)] public void* D3D11DeviceContext;
[MemberFunction("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 0F B6 83 ?? ?? ?? ?? 3C 01 73 70")]
public partial void ExecuteCommands();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
namespace FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;

/// <summary>
/// Contains blending configuration for the renderer.
/// </summary>
/// <remarks>
/// 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
/// <see cref="ImmediateContext"/> as needed.
/// </remarks>
[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<bool>(nameof(BlendEnable), 0, 1)]
[BitField<byte>(nameof(BlendOpMinusOne), 1, 3)]
[BitField<byte>(nameof(SrcBlendMinusOne), 4, 4)]
[BitField<byte>(nameof(DestBlendMinusOne), 8, 4)]
[BitField<byte>(nameof(BlendOpAlphaMinusOne), 12, 3)]
[BitField<byte>(nameof(SrcBlendAlphaMinusOne), 15, 4)]
[BitField<byte>(nameof(DestBlendAlphaMinusOne), 19, 4)]
[BitField<byte>(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);
}
}
Loading
Loading