diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs index 88294fdee..2e8f4416b 100644 --- a/Dalamud/Interface/Internal/UiDebug.cs +++ b/Dalamud/Interface/Internal/UiDebug.cs @@ -1,7 +1,9 @@ using System.Numerics; +using Dalamud.Game; using Dalamud.Game.Gui; using Dalamud.Interface.ImGuiSeStringRenderer.Internal; +using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Utility; using Dalamud.Utility; @@ -317,6 +319,32 @@ internal unsafe class UiDebug ImGui.TreePop(); } } + + if (ImGui.Button($"Replace with a random image##{(ulong)textureInfo:X}")) + { + var texm = Service.Get(); + texm.Shared + .GetFromGame( + Random.Shared.Next(0, 1) == 0 + ? $"ui/loadingimage/-nowloading_base{Random.Shared.Next(1, 33)}.tex" + : $"ui/loadingimage/-nowloading_base{Random.Shared.Next(1, 33)}_hr1.tex") + .RentAsync() + .ContinueWith( + r => Service.Get().RunOnFrameworkThread( + () => + { + if (!r.IsCompletedSuccessfully) + return; + + using (r.Result) + { + textureInfo->AtkTexture.ReleaseTexture(); + textureInfo->AtkTexture.KernelTexture = + texm.ConvertToKernelTexture(r.Result); + textureInfo->AtkTexture.TextureType = TextureType.KernelTexture; + } + })); + } } } else diff --git a/Dalamud/Interface/Textures/ISharedImmediateTexture.cs b/Dalamud/Interface/Textures/ISharedImmediateTexture.cs index 591b9846c..0ceb92171 100644 --- a/Dalamud/Interface/Textures/ISharedImmediateTexture.cs +++ b/Dalamud/Interface/Textures/ISharedImmediateTexture.cs @@ -25,7 +25,8 @@ public interface ISharedImmediateTexture /// s may be cached, but the performance benefit will be minimal. /// Calling outside the main thread will fail. /// This function does not throw. - /// will be ignored. + /// will be ignored, including the cases when the returned texture wrap + /// is passed to a function with leaveWrapOpen parameter. /// If the texture is unavailable for any reason, then the returned instance of /// will point to an empty texture instead. /// @@ -42,7 +43,8 @@ public interface ISharedImmediateTexture /// s may be cached, but the performance benefit will be minimal. /// Calling outside the main thread will fail. /// This function does not throw. - /// will be ignored. + /// will be ignored, including the cases when the returned texture wrap + /// is passed to a function with leaveWrapOpen parameter. /// If the texture is unavailable for any reason, then will be returned. /// [return: NotNullIfNotNull(nameof(defaultWrap))] @@ -59,7 +61,8 @@ public interface ISharedImmediateTexture /// s may be cached, but the performance benefit will be minimal. /// Calling outside the main thread will fail. /// This function does not throw. - /// on the returned will be ignored. + /// on the returned will be ignored, including + /// the cases when the returned texture wrap is passed to a function with leaveWrapOpen parameter. /// /// Thrown when called outside the UI thread. bool TryGetWrap([NotNullWhen(true)] out IDalamudTextureWrap? texture, out Exception? exception); diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs b/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs index a744114e8..2ce96e59d 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Dalamud.Interface.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Textures.TextureWraps.Internal; using Dalamud.Plugin.Internal.Types; @@ -10,6 +9,8 @@ using Dalamud.Plugin.Services; using Dalamud.Utility; using Dalamud.Utility.TerraFxCom; +using Lumina.Data.Files; + using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -18,6 +19,72 @@ namespace Dalamud.Interface.Textures.Internal; /// Service responsible for loading and disposing ImGui texture wraps. internal sealed partial class TextureManager { + /// + unsafe nint ITextureProvider.ConvertToKernelTexture(IDalamudTextureWrap wrap, bool leaveWrapOpen) => + (nint)this.ConvertToKernelTexture(wrap, leaveWrapOpen); + + /// + public unsafe FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture* ConvertToKernelTexture( + IDalamudTextureWrap wrap, + bool leaveWrapOpen = false) + { + using var wrapAux = new WrapAux(wrap, leaveWrapOpen); + + var flags = TexFile.Attribute.TextureType2D; + if (wrapAux.Desc.Usage == D3D11_USAGE.D3D11_USAGE_IMMUTABLE) + flags |= TexFile.Attribute.Immutable; + if (wrapAux.Desc.Usage == D3D11_USAGE.D3D11_USAGE_DYNAMIC) + flags |= TexFile.Attribute.ReadWrite; + if ((wrapAux.Desc.CPUAccessFlags & (uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ) != 0) + flags |= TexFile.Attribute.CpuRead; + if ((wrapAux.Desc.BindFlags & (uint)D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET) != 0) + flags |= TexFile.Attribute.TextureRenderTarget; + if ((wrapAux.Desc.BindFlags & (uint)D3D11_BIND_FLAG.D3D11_BIND_DEPTH_STENCIL) != 0) + flags |= TexFile.Attribute.TextureDepthStencil; + if (wrapAux.Desc.ArraySize != 1) + throw new NotSupportedException("TextureArray2D is currently not supported."); + + var gtex = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture.CreateTexture2D( + (int)wrapAux.Desc.Width, + (int)wrapAux.Desc.Height, + (byte)wrapAux.Desc.MipLevels, + (uint)TexFile.TextureFormat.Null, // instructs the game to skip preprocessing it seems + (uint)flags, + 0); + + // Kernel::Texture owns these resources. We're passing the ownership to them. + wrapAux.TexPtr->AddRef(); + wrapAux.SrvPtr->AddRef(); + + // Not sure this is needed + var ltf = wrapAux.Desc.Format switch + { + DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT => TexFile.TextureFormat.R32G32B32A32F, + DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT => TexFile.TextureFormat.R16G16B16A16F, + DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT => TexFile.TextureFormat.R32G32F, + DXGI_FORMAT.DXGI_FORMAT_R16G16_FLOAT => TexFile.TextureFormat.R16G16F, + DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT => TexFile.TextureFormat.R32F, + DXGI_FORMAT.DXGI_FORMAT_R24G8_TYPELESS => TexFile.TextureFormat.D24S8, + DXGI_FORMAT.DXGI_FORMAT_R16_TYPELESS => TexFile.TextureFormat.D16, + DXGI_FORMAT.DXGI_FORMAT_A8_UNORM => TexFile.TextureFormat.A8, + DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM => TexFile.TextureFormat.BC1, + DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM => TexFile.TextureFormat.BC2, + DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM => TexFile.TextureFormat.BC3, + DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM => TexFile.TextureFormat.BC5, + DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM => TexFile.TextureFormat.B4G4R4A4, + DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM => TexFile.TextureFormat.B5G5R5A1, + DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM => TexFile.TextureFormat.B8G8R8A8, + DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM => TexFile.TextureFormat.B8G8R8X8, + DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM => TexFile.TextureFormat.BC7, + _ => TexFile.TextureFormat.Null, + }; + gtex->TextureFormat = (FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.TextureFormat)ltf; + + gtex->D3D11Texture2D = wrapAux.TexPtr; + gtex->D3D11ShaderResourceView = wrapAux.SrvPtr; + return gtex; + } + /// bool ITextureProvider.IsDxgiFormatSupportedForCreateFromExistingTextureAsync(int dxgiFormat) => this.IsDxgiFormatSupportedForCreateFromExistingTextureAsync((DXGI_FORMAT)dxgiFormat); diff --git a/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs b/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs index 68e2dde47..c62ad61b4 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManagerPluginScoped.cs @@ -134,6 +134,10 @@ internal sealed class TextureManagerPluginScoped : $"{nameof(TextureManagerPluginScoped)}({this.plugin.Name})"; } + /// + public unsafe nint ConvertToKernelTexture(IDalamudTextureWrap wrap, bool leaveWrapOpen = false) => + (nint)this.ManagerOrThrow.ConvertToKernelTexture(wrap, leaveWrapOpen); + /// public IDalamudTextureWrap CreateEmpty( RawImageSpecification specs, diff --git a/Dalamud/Plugin/Services/ITextureProvider.cs b/Dalamud/Plugin/Services/ITextureProvider.cs index d914b1091..ff13f11f1 100644 --- a/Dalamud/Plugin/Services/ITextureProvider.cs +++ b/Dalamud/Plugin/Services/ITextureProvider.cs @@ -5,7 +5,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Dalamud.Interface.Internal; using Dalamud.Interface.Internal.Windows.Data.Widgets; using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; @@ -281,4 +280,20 @@ public interface ITextureProvider /// true if supported. /// This function does not throw exceptions. bool IsDxgiFormatSupportedForCreateFromExistingTextureAsync(int dxgiFormat); + + /// Converts an existing instance to a new instance of + /// which can be used to supply a custom + /// texture onto an in-game addon (UI element.) + /// Instance of to convert. + /// Whether to leave non-disposed when the returned + /// completes. + /// Address of the new . + /// See PrintTextureInfo in for an example + /// of replacing the texture of an image node. + /// + /// If the returned kernel texture is to be destroyed, call the fourth function in its vtable, by calling + /// or + /// ((delegate* unmanaged<nint, void>)(*(nint**)ptr)[3](ptr). + /// + nint ConvertToKernelTexture(IDalamudTextureWrap wrap, bool leaveWrapOpen = false); } diff --git a/Dalamud/Plugin/Services/ITextureReadbackProvider.cs b/Dalamud/Plugin/Services/ITextureReadbackProvider.cs index 309be103a..b41ded41f 100644 --- a/Dalamud/Plugin/Services/ITextureReadbackProvider.cs +++ b/Dalamud/Plugin/Services/ITextureReadbackProvider.cs @@ -22,6 +22,9 @@ public interface ITextureReadbackProvider /// /// The length of the returned RawData may not match /// * . + /// may not be the minimal value required to represent the texture + /// bitmap data. For example, if a texture is 4x4 B8G8R8A8, the minimal pitch would be 32, but the function may + /// return 64 instead. /// This function may throw an exception. /// Task<(RawImageSpecification Specification, byte[] RawData)> GetRawImageAsync(