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