diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs index 769df073b..6de891189 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs @@ -53,6 +53,8 @@ internal class TexWidget : IDataWindowWidget private FileDialogManager fileDialogManager = null!; private ExistingTextureModificationArgs existingTextureModificationArgs; + private ImGuiViewportTextureArgs viewportTextureArgs; + private int viewportIndexInt; private string[]? supportedRenderTargetFormatNames; private DXGI_FORMAT[]? supportedRenderTargetFormats; private int renderTargetChoiceInt; @@ -84,6 +86,7 @@ internal class TexWidget : IDataWindowWidget this.inputManifestResourceNameIndex = 0; this.supportedRenderTargetFormats = null; this.supportedRenderTargetFormatNames = null; + this.renderTargetChoiceInt = 0; this.fileDialogManager = new(); this.existingTextureModificationArgs = new() { @@ -92,6 +95,8 @@ internal class TexWidget : IDataWindowWidget NewWidth = 320, NewHeight = 240, }; + this.viewportTextureArgs = default; + this.viewportIndexInt = 0; this.Ready = true; } @@ -135,57 +140,41 @@ internal class TexWidget : IDataWindowWidget ImGui.Dummy(new(ImGui.GetTextLineHeightWithSpacing())); - ImGui.AlignTextToFramePadding(); - ImGui.TextUnformatted("Capture: "); - if (ImGui.Button("Game")) - this.addedTextures.Add(new() { Api10 = this.textureManager.CreateFromGameScreen() }); - - ImGui.SameLine(); - if (ImGui.Button("Game (Auto)")) - this.addedTextures.Add(new() { Api10 = this.textureManager.CreateFromGameScreen(true) }); - - ImGui.SameLine(); - if (ImGui.Button("Main Viewport")) - { - this.addedTextures.Add( - new() { Api10 = this.textureManager.CreateFromImGuiViewport(ImGui.GetMainViewport().ID) }); - } - - ImGui.SameLine(); - if (ImGui.Button("Main Viewport (Auto)")) - { - this.addedTextures.Add( - new() { Api10 = this.textureManager.CreateFromImGuiViewport(ImGui.GetMainViewport().ID, true) }); - } - - if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromGameIcon), ImGuiTreeNodeFlags.DefaultOpen)) + if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromGameIcon))) { ImGui.PushID(nameof(this.DrawGetFromGameIcon)); this.DrawGetFromGameIcon(); ImGui.PopID(); } - if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromGame), ImGuiTreeNodeFlags.DefaultOpen)) + if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromGame))) { ImGui.PushID(nameof(this.DrawGetFromGame)); this.DrawGetFromGame(); ImGui.PopID(); } - if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromFile), ImGuiTreeNodeFlags.DefaultOpen)) + if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromFile))) { ImGui.PushID(nameof(this.DrawGetFromFile)); this.DrawGetFromFile(); ImGui.PopID(); } - if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromManifestResource), ImGuiTreeNodeFlags.DefaultOpen)) + if (ImGui.CollapsingHeader(nameof(ITextureProvider.GetFromManifestResource))) { ImGui.PushID(nameof(this.DrawGetFromManifestResource)); this.DrawGetFromManifestResource(); ImGui.PopID(); } + if (ImGui.CollapsingHeader(nameof(ITextureProvider.CreateFromImGuiViewportAsync))) + { + ImGui.PushID(nameof(this.DrawCreateFromImGuiViewportAsync)); + this.DrawCreateFromImGuiViewportAsync(); + ImGui.PopID(); + } + if (ImGui.CollapsingHeader("UV")) { ImGui.PushID(nameof(this.DrawUvInput)); @@ -574,6 +563,57 @@ internal class TexWidget : IDataWindowWidget ImGuiHelpers.ScaledDummy(10); } + private void DrawCreateFromImGuiViewportAsync() + { + var viewports = ImGui.GetPlatformIO().Viewports; + if (ImGui.BeginCombo( + nameof(this.viewportTextureArgs.ViewportId), + $"{this.viewportIndexInt}. {viewports[this.viewportIndexInt].ID:X08}")) + { + for (var i = 0; i < viewports.Size; i++) + { + var sel = this.viewportIndexInt == i; + if (ImGui.Selectable($"#{i}: {viewports[i].ID:X08}", ref sel)) + { + this.viewportIndexInt = i; + ImGui.SetItemDefaultFocus(); + } + } + + ImGui.EndCombo(); + } + + var b = this.viewportTextureArgs.KeepTransparency; + if (ImGui.Checkbox(nameof(this.viewportTextureArgs.KeepTransparency), ref b)) + this.viewportTextureArgs.KeepTransparency = b; + + b = this.viewportTextureArgs.AutoUpdate; + if (ImGui.Checkbox(nameof(this.viewportTextureArgs.AutoUpdate), ref b)) + this.viewportTextureArgs.AutoUpdate = b; + + b = this.viewportTextureArgs.TakeBeforeImGuiRender; + if (ImGui.Checkbox(nameof(this.viewportTextureArgs.TakeBeforeImGuiRender), ref b)) + this.viewportTextureArgs.TakeBeforeImGuiRender = b; + + var vec2 = this.viewportTextureArgs.Uv0; + if (ImGui.InputFloat2(nameof(this.viewportTextureArgs.Uv0), ref vec2)) + this.viewportTextureArgs.Uv0 = vec2; + + vec2 = this.viewportTextureArgs.Uv1; + if (ImGui.InputFloat2(nameof(this.viewportTextureArgs.Uv1), ref vec2)) + this.viewportTextureArgs.Uv1 = vec2; + + if (ImGui.Button("Create") && this.viewportIndexInt >= 0 && this.viewportIndexInt < viewports.Size) + { + this.addedTextures.Add( + new() + { + Api10 = this.textureManager.CreateFromImGuiViewportAsync( + this.viewportTextureArgs with { ViewportId = viewports[this.viewportIndexInt].ID }), + }); + } + } + private void DrawUvInput() { ImGui.InputFloat2("UV0", ref this.inputTexUv0); @@ -586,25 +626,8 @@ internal class TexWidget : IDataWindowWidget private void DrawExistingTextureModificationArgs() { - var vec2 = this.existingTextureModificationArgs.Uv0; - if (ImGui.InputFloat2("UV0", ref vec2)) - this.existingTextureModificationArgs.Uv0 = vec2; - - vec2 = this.existingTextureModificationArgs.Uv1; - if (ImGui.InputFloat2("UV1", ref vec2)) - this.existingTextureModificationArgs.Uv1 = vec2; - - Span wh = stackalloc int[2]; - wh[0] = this.existingTextureModificationArgs.NewWidth; - wh[1] = this.existingTextureModificationArgs.NewHeight; - if (ImGui.InputInt2("New Size", ref wh[0])) - { - this.existingTextureModificationArgs.NewWidth = wh[0]; - this.existingTextureModificationArgs.NewHeight = wh[1]; - } - var b = this.existingTextureModificationArgs.MakeOpaque; - if (ImGui.Checkbox("Make Opaque", ref b)) + if (ImGui.Checkbox(nameof(this.existingTextureModificationArgs.MakeOpaque), ref b)) this.existingTextureModificationArgs.MakeOpaque = b; if (this.supportedRenderTargetFormats is null) @@ -619,11 +642,30 @@ internal class TexWidget : IDataWindowWidget this.supportedRenderTargetFormatNames ??= this.supportedRenderTargetFormats.Select(Enum.GetName).ToArray(); ImGui.Combo( - "Format", + nameof(this.existingTextureModificationArgs.DxgiFormat), ref this.renderTargetChoiceInt, this.supportedRenderTargetFormatNames, this.supportedRenderTargetFormatNames.Length); - + + Span wh = stackalloc int[2]; + wh[0] = this.existingTextureModificationArgs.NewWidth; + wh[1] = this.existingTextureModificationArgs.NewHeight; + if (ImGui.InputInt2( + $"{nameof(this.existingTextureModificationArgs.NewWidth)}/{nameof(this.existingTextureModificationArgs.NewHeight)}", + ref wh[0])) + { + this.existingTextureModificationArgs.NewWidth = wh[0]; + this.existingTextureModificationArgs.NewHeight = wh[1]; + } + + var vec2 = this.existingTextureModificationArgs.Uv0; + if (ImGui.InputFloat2(nameof(this.existingTextureModificationArgs.Uv0), ref vec2)) + this.existingTextureModificationArgs.Uv0 = vec2; + + vec2 = this.existingTextureModificationArgs.Uv1; + if (ImGui.InputFloat2(nameof(this.existingTextureModificationArgs.Uv1), ref vec2)) + this.existingTextureModificationArgs.Uv1 = vec2; + ImGuiHelpers.ScaledDummy(10); } diff --git a/Dalamud/Interface/Textures/ExistingTextureModificationArgs.cs b/Dalamud/Interface/Textures/ExistingTextureModificationArgs.cs index 9bcb9f34d..9c70b52cc 100644 --- a/Dalamud/Interface/Textures/ExistingTextureModificationArgs.cs +++ b/Dalamud/Interface/Textures/ExistingTextureModificationArgs.cs @@ -9,15 +9,18 @@ namespace Dalamud.Interface.Textures; /// Describes how to modify an existing texture. public record struct ExistingTextureModificationArgs() { - /// Gets or sets the left top coordinates relative to the size of the source texture. - /// Coordinates should be in range between 0 and 1. - public Vector2 Uv0 { get; set; } = Vector2.Zero; + /// Gets or sets a value indicating whether to make the texture opaque. + /// If true, then the alpha channel values will be filled with 1.0. + public bool MakeOpaque { get; set; } = false; - /// Gets or sets the right bottom coordinates relative to the size of the source texture. - /// Coordinates should be in range between 0 and 1. - /// If set to , then it will be interpreted as , - /// to accommodate the use of default value of this record struct. - public Vector2 Uv1 { get; set; } = Vector2.One; + /// Gets or sets the new DXGI format. + /// + /// Set to 0 () to use the source pixel format. + /// Supported values can be queried with + /// . This may not necessarily + /// match . + /// + public int DxgiFormat { get; set; } = (int)DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM; /// Gets or sets the new width. /// Set to 0 to automatically calculate according to the original texture size, , and @@ -29,18 +32,15 @@ public record struct ExistingTextureModificationArgs() /// . public int NewHeight { get; set; } - /// Gets or sets a value indicating whether to make the texture opaque. - /// Alpha channel values will be filled with 1.0. - public bool MakeOpaque { get; set; } = false; + /// Gets or sets the left top coordinates relative to the size of the source texture. + /// Coordinates should be in range between 0 and 1. + public Vector2 Uv0 { get; set; } = Vector2.Zero; - /// Gets or sets the new DXGI format. - /// - /// Set to 0 () to use the source pixel format. - /// Supported values can be queried with - /// . This may not necessarily - /// match . - /// - public int DxgiFormat { get; set; } = (int)DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM; + /// Gets or sets the right bottom coordinates relative to the size of the source texture. + /// Coordinates should be in range between 0 and 1. + /// If set to , then it will be interpreted as , + /// to accommodate the use of default value of this record struct. + public Vector2 Uv1 { get; set; } = Vector2.One; /// Gets or sets the format (typed). internal DXGI_FORMAT Format diff --git a/Dalamud/Interface/Textures/ImGuiViewportTextureArgs.cs b/Dalamud/Interface/Textures/ImGuiViewportTextureArgs.cs new file mode 100644 index 000000000..b53bd0f48 --- /dev/null +++ b/Dalamud/Interface/Textures/ImGuiViewportTextureArgs.cs @@ -0,0 +1,68 @@ +using System.Numerics; + +using Dalamud.Interface.Internal; + +using ImGuiNET; + +namespace Dalamud.Interface.Textures; + +/// Describes how to take a texture of an existing ImGui viewport. +public record struct ImGuiViewportTextureArgs() +{ + /// Gets or sets the ImGui Viewport ID to capture. + /// Use from to take the main viewport, + /// where the game renders to. + public uint ViewportId { get; set; } + + /// Gets or sets a value indicating whether to automatically update the texture. + /// Enabling this will also update as needed. + public bool AutoUpdate { get; set; } + + /// Gets or sets a value indicating whether to get the texture before rendering ImGui. + /// It probably makes no sense to enable this unless points to the main viewport. + /// + public bool TakeBeforeImGuiRender { get; set; } + + /// Gets or sets a value indicating whether to keep the transparency. + /// + /// If true, then the alpha channel values will be filled with 1.0. + /// Keep in mind that screen captures generally do not need alpha values. + /// + // Intentionally not "MakeOpaque", to accommodate the use of default value of this record struct. + public bool KeepTransparency { get; set; } = false; + + /// Gets or sets the left top coordinates relative to the size of the source texture. + /// Coordinates should be in range between 0 and 1. + public Vector2 Uv0 { get; set; } = Vector2.Zero; + + /// Gets or sets the right bottom coordinates relative to the size of the source texture. + /// Coordinates should be in range between 0 and 1. + /// If set to , then it will be interpreted as , + /// to accommodate the use of default value of this record struct. + public Vector2 Uv1 { get; set; } = Vector2.One; + + /// Gets the effective value of . + internal Vector2 Uv1Effective => this.Uv1 == Vector2.Zero ? Vector2.One : this.Uv1; + + /// Checks the properties and throws an exception if values are invalid. + internal void ThrowOnInvalidValues() + { + if (this.Uv0.X is < 0 or > 1 or float.NaN) + throw new ArgumentException($"{nameof(this.Uv0)}.X is out of range."); + + if (this.Uv0.Y is < 0 or > 1 or float.NaN) + throw new ArgumentException($"{nameof(this.Uv0)}.Y is out of range."); + + if (this.Uv1Effective.X is < 0 or > 1 or float.NaN) + throw new ArgumentException($"{nameof(this.Uv1)}.X is out of range."); + + if (this.Uv1Effective.Y is < 0 or > 1 or float.NaN) + throw new ArgumentException($"{nameof(this.Uv1)}.Y is out of range."); + + if (this.Uv0.X >= this.Uv1Effective.X || this.Uv0.Y >= this.Uv1Effective.Y) + { + throw new ArgumentException( + $"{nameof(this.Uv0)} must be strictly less than {nameof(this.Uv1)} in a componentwise way."); + } + } +} diff --git a/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs b/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs index 56b0356e5..067368ce5 100644 --- a/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs +++ b/Dalamud/Interface/Textures/Internal/TextureManager.FromExistingTexture.cs @@ -5,8 +5,6 @@ using Dalamud.Interface.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; -using ImGuiNET; - using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -85,47 +83,15 @@ internal sealed partial class TextureManager } /// - public Task CreateFromGameScreen( - bool autoUpdate = false, - CancellationToken cancellationToken = default) => - this.interfaceManager.RunBeforeImGuiRender( - () => - { - cancellationToken.ThrowIfCancellationRequested(); - var t = new ViewportTextureWrap(ImGui.GetMainViewport().ID, true, autoUpdate, cancellationToken); - t.Update(); - try - { - return t.FirstUpdateTask.Result; - } - catch - { - t.Dispose(); - throw; - } - }); - - /// - public Task CreateFromImGuiViewport( - uint viewportId, - bool autoUpdate = false, - CancellationToken cancellationToken = default) => - this.interfaceManager.RunBeforeImGuiRender( - () => - { - cancellationToken.ThrowIfCancellationRequested(); - var t = new ViewportTextureWrap(viewportId, false, autoUpdate, cancellationToken); - t.Update(); - try - { - return t.FirstUpdateTask.Result; - } - catch - { - t.Dispose(); - throw; - } - }); + public async Task CreateFromImGuiViewportAsync( + ImGuiViewportTextureArgs args, + CancellationToken cancellationToken = default) + { + // This constructor may throw; keep the function "async", to wrap the exception as a Task. + var t = new ViewportTextureWrap(args, cancellationToken); + t.QueueUpdate(); + return await t.FirstUpdateTask; + } /// public async Task<(RawImageSpecification Specification, byte[] RawData)> GetRawDataFromExistingTextureAsync( diff --git a/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs b/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs index 8a0a17183..28e8f6e3d 100644 --- a/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs +++ b/Dalamud/Interface/Textures/Internal/ViewportTextureWrap.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Numerics; using System.Threading; using System.Threading.Tasks; @@ -19,33 +18,25 @@ namespace Dalamud.Interface.Textures.Internal; /// A texture wrap that takes its buffer from the frame buffer (of swap chain). internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDisposable { - private readonly uint viewportId; - private readonly bool beforeImGuiRender; private readonly CancellationToken cancellationToken; private readonly TaskCompletionSource firstUpdateTaskCompletionSource = new(); + private ImGuiViewportTextureArgs args; private D3D11_TEXTURE2D_DESC desc; private ComPtr tex; private ComPtr srv; private ComPtr rtv; - private bool autoUpdate; private bool disposed; /// Initializes a new instance of the class. - /// The source viewport ID. - /// Capture before calling . - /// If true, automatically update the underlying texture. + /// The arguments for creating a texture. /// The cancellation token. - public ViewportTextureWrap( - uint viewportId, - bool beforeImGuiRender, - bool autoUpdate, - CancellationToken cancellationToken) + public ViewportTextureWrap(ImGuiViewportTextureArgs args, CancellationToken cancellationToken) { - this.viewportId = viewportId; - this.beforeImGuiRender = beforeImGuiRender; - this.autoUpdate = autoUpdate; + args.ThrowOnInvalidValues(); + + this.args = args; this.cancellationToken = cancellationToken; } @@ -77,7 +68,7 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos { ThreadSafety.AssertMainThread(); - using var backBuffer = GetImGuiViewportBackBuffer(this.viewportId); + using var backBuffer = GetImGuiViewportBackBuffer(this.args.ViewportId); D3D11_TEXTURE2D_DESC newDesc; backBuffer.Get()->GetDesc(&newDesc); @@ -90,14 +81,24 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos using var context = default(ComPtr); device.Get()->GetImmediateContext(context.GetAddressOf()); - if (this.desc.Width != newDesc.Width - || this.desc.Height != newDesc.Height + var copyBox = new D3D11_BOX + { + left = (uint)MathF.Round(newDesc.Width * this.args.Uv0.X), + top = (uint)MathF.Round(newDesc.Height * this.args.Uv0.Y), + right = (uint)MathF.Round(newDesc.Width * this.args.Uv1Effective.X), + bottom = (uint)MathF.Round(newDesc.Height * this.args.Uv1Effective.Y), + front = 0, + back = 1, + }; + + if (this.desc.Width != copyBox.right - copyBox.left + || this.desc.Height != copyBox.bottom - copyBox.top || this.desc.Format != newDesc.Format) { var texDesc = new D3D11_TEXTURE2D_DESC { - Width = newDesc.Width, - Height = newDesc.Height, + Width = copyBox.right - copyBox.left, + Height = copyBox.bottom - copyBox.top, MipLevels = 1, ArraySize = 1, Format = newDesc.Format, @@ -131,19 +132,32 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos srvTemp.GetAddressOf()) .ThrowOnError(); - this.desc = newDesc; + this.desc = texDesc; srvTemp.Swap(ref this.srv); rtvTemp.Swap(ref this.rtv); texTemp.Swap(ref this.tex); } - context.Get()->CopyResource((ID3D11Resource*)this.tex.Get(), (ID3D11Resource*)backBuffer.Get()); - var rtvLocal = this.rtv.Get(); - context.Get()->OMSetRenderTargets(1u, &rtvLocal, null); - Service.Get().SimpleDrawer.StripAlpha(context.Get()); + // context.Get()->CopyResource((ID3D11Resource*)this.tex.Get(), (ID3D11Resource*)backBuffer.Get()); + context.Get()->CopySubresourceRegion( + (ID3D11Resource*)this.tex.Get(), + 0, + 0, + 0, + 0, + (ID3D11Resource*)backBuffer.Get(), + 0, + ©Box); - var dummy = default(ID3D11RenderTargetView*); - context.Get()->OMSetRenderTargets(1u, &dummy, null); + if (!this.args.KeepTransparency) + { + var rtvLocal = this.rtv.Get(); + context.Get()->OMSetRenderTargets(1u, &rtvLocal, null); + Service.Get().SimpleDrawer.StripAlpha(context.Get()); + + var dummy = default(ID3D11RenderTargetView*); + context.Get()->OMSetRenderTargets(1u, &dummy, null); + } this.firstUpdateTaskCompletionSource.TrySetResult(this); } @@ -152,20 +166,22 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos this.firstUpdateTaskCompletionSource.TrySetException(e); } - if (this.autoUpdate) - { - Service.Get().RunOnTick( - () => - { - if (this.beforeImGuiRender) - Service.Get().RunBeforeImGuiRender(this.Update); - else - Service.Get().RunAfterImGuiRender(this.Update); - }, - cancellationToken: this.cancellationToken); - } + if (this.args.AutoUpdate) + this.QueueUpdate(); } + /// Queues a call to . + public void QueueUpdate() => + Service.Get().RunOnTick( + () => + { + if (this.args.TakeBeforeImGuiRender) + Service.Get().RunBeforeImGuiRender(this.Update); + else + Service.Get().RunAfterImGuiRender(this.Update); + }, + cancellationToken: this.cancellationToken); + /// Queue the texture to be disposed once the frame ends. public void Dispose() { @@ -230,7 +246,7 @@ internal sealed class ViewportTextureWrap : IDalamudTextureWrap, IDeferredDispos private void Dispose(bool disposing) { this.disposed = true; - this.autoUpdate = false; + this.args.AutoUpdate = false; if (disposing) Service.GetNullable()?.EnqueueDeferredDispose(this); else diff --git a/Dalamud/Plugin/Services/ITextureProvider.cs b/Dalamud/Plugin/Services/ITextureProvider.cs index 79b2a7ad3..f007b4649 100644 --- a/Dalamud/Plugin/Services/ITextureProvider.cs +++ b/Dalamud/Plugin/Services/ITextureProvider.cs @@ -45,27 +45,16 @@ public partial interface ITextureProvider bool leaveWrapOpen = false, CancellationToken cancellationToken = default); - /// Creates a texture from the game screen, before rendering Dalamud. - /// If true, automatically update the underlying texture. - /// The cancellation token. - /// A containing the copied texture on success. Dispose after use. - /// This function may throw an exception. - Task CreateFromGameScreen( - bool autoUpdate = false, - CancellationToken cancellationToken = default); - - /// Creates a texture from the game screen, before rendering Dalamud. - /// The viewport ID. - /// If true, automatically update the underlying texture. + /// Creates a texture from an ImGui viewport. + /// The arguments for creating a texture. /// The cancellation token. /// A containing the copied texture on success. Dispose after use. /// /// Use ImGui.GetMainViewport().ID to capture the game screen with Dalamud rendered. /// This function may throw an exception. /// - Task CreateFromImGuiViewport( - uint viewportId, - bool autoUpdate = false, + Task CreateFromImGuiViewportAsync( + ImGuiViewportTextureArgs args, CancellationToken cancellationToken = default); /// Gets a texture from the given bytes, trying to interpret it as a .tex file or other well-known image