mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add TextureArraySlicer
This commit is contained in:
parent
4454ac48da
commit
069b28272b
3 changed files with 145 additions and 13 deletions
119
Penumbra/Interop/Services/TextureArraySlicer.cs
Normal file
119
Penumbra/Interop/Services/TextureArraySlicer.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using SharpDX.Direct3D;
|
||||||
|
using SharpDX.Direct3D11;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates ImGui handles over slices of array textures, and manages their lifetime.
|
||||||
|
/// </summary>
|
||||||
|
public sealed unsafe class TextureArraySlicer : IUiService, IDisposable
|
||||||
|
{
|
||||||
|
private const uint InitialTimeToLive = 2;
|
||||||
|
|
||||||
|
private readonly Dictionary<(nint XivTexture, byte SliceIndex), SliceState> _activeSlices = [];
|
||||||
|
private readonly HashSet<(nint XivTexture, byte SliceIndex)> _expiredKeys = [];
|
||||||
|
|
||||||
|
/// <remarks> Caching this across frames will cause a crash to desktop. </remarks>
|
||||||
|
public nint GetImGuiHandle(Texture* texture, byte sliceIndex)
|
||||||
|
{
|
||||||
|
if (texture == null)
|
||||||
|
throw new ArgumentNullException(nameof(texture));
|
||||||
|
if (sliceIndex >= texture->ArraySize)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(sliceIndex), $"Slice index ({sliceIndex}) is greater than or equal to the texture array size ({texture->ArraySize})");
|
||||||
|
if (_activeSlices.TryGetValue(((nint)texture, sliceIndex), out var state))
|
||||||
|
{
|
||||||
|
state.Refresh();
|
||||||
|
return (nint)state.ShaderResourceView;
|
||||||
|
}
|
||||||
|
var srv = (ShaderResourceView)(nint)texture->D3D11ShaderResourceView;
|
||||||
|
var description = srv.Description;
|
||||||
|
switch (description.Dimension)
|
||||||
|
{
|
||||||
|
case ShaderResourceViewDimension.Texture1D:
|
||||||
|
case ShaderResourceViewDimension.Texture2D:
|
||||||
|
case ShaderResourceViewDimension.Texture2DMultisampled:
|
||||||
|
case ShaderResourceViewDimension.Texture3D:
|
||||||
|
case ShaderResourceViewDimension.TextureCube:
|
||||||
|
// This function treats these as single-slice arrays.
|
||||||
|
// As per the range check above, the only valid slice (i. e. 0) has been requested, therefore there is nothing to do.
|
||||||
|
break;
|
||||||
|
case ShaderResourceViewDimension.Texture1DArray:
|
||||||
|
description.Texture1DArray.FirstArraySlice = sliceIndex;
|
||||||
|
description.Texture2DArray.ArraySize = 1;
|
||||||
|
break;
|
||||||
|
case ShaderResourceViewDimension.Texture2DArray:
|
||||||
|
description.Texture2DArray.FirstArraySlice = sliceIndex;
|
||||||
|
description.Texture2DArray.ArraySize = 1;
|
||||||
|
break;
|
||||||
|
case ShaderResourceViewDimension.Texture2DMultisampledArray:
|
||||||
|
description.Texture2DMSArray.FirstArraySlice = sliceIndex;
|
||||||
|
description.Texture2DMSArray.ArraySize = 1;
|
||||||
|
break;
|
||||||
|
case ShaderResourceViewDimension.TextureCubeArray:
|
||||||
|
description.TextureCubeArray.First2DArrayFace = sliceIndex * 6;
|
||||||
|
description.TextureCubeArray.CubeCount = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"{nameof(TextureArraySlicer)} does not support dimension {description.Dimension}");
|
||||||
|
}
|
||||||
|
state = new SliceState(new ShaderResourceView(srv.Device, srv.Resource, description));
|
||||||
|
_activeSlices.Add(((nint)texture, sliceIndex), state);
|
||||||
|
return (nint)state.ShaderResourceView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Tick()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var (key, slice) in _activeSlices)
|
||||||
|
{
|
||||||
|
if (!slice.Tick())
|
||||||
|
_expiredKeys.Add(key);
|
||||||
|
}
|
||||||
|
foreach (var key in _expiredKeys)
|
||||||
|
{
|
||||||
|
_activeSlices.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_expiredKeys.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var slice in _activeSlices.Values)
|
||||||
|
{
|
||||||
|
slice.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class SliceState(ShaderResourceView shaderResourceView) : IDisposable
|
||||||
|
{
|
||||||
|
public readonly ShaderResourceView ShaderResourceView = shaderResourceView;
|
||||||
|
|
||||||
|
private uint _timeToLive = InitialTimeToLive;
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
_timeToLive = InitialTimeToLive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Tick()
|
||||||
|
{
|
||||||
|
if (unchecked(_timeToLive--) > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ShaderResourceView.Dispose();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ShaderResourceView.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -72,6 +72,14 @@
|
||||||
<HintPath>$(DalamudLibPath)Iced.dll</HintPath>
|
<HintPath>$(DalamudLibPath)Iced.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="SharpDX">
|
||||||
|
<HintPath>$(DalamudLibPath)SharpDX.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SharpDX.Direct3D11">
|
||||||
|
<HintPath>$(DalamudLibPath)SharpDX.Direct3D11.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="OtterTex.dll">
|
<Reference Include="OtterTex.dll">
|
||||||
<HintPath>lib\OtterTex.dll</HintPath>
|
<HintPath>lib\OtterTex.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
|
using Penumbra.Interop.Services;
|
||||||
using Penumbra.UI.AdvancedWindow;
|
using Penumbra.UI.AdvancedWindow;
|
||||||
using Penumbra.UI.Knowledge;
|
using Penumbra.UI.Knowledge;
|
||||||
using Penumbra.UI.Tabs.Debug;
|
using Penumbra.UI.Tabs.Debug;
|
||||||
|
|
@ -10,23 +11,25 @@ namespace Penumbra.UI;
|
||||||
|
|
||||||
public class PenumbraWindowSystem : IDisposable, IUiService
|
public class PenumbraWindowSystem : IDisposable, IUiService
|
||||||
{
|
{
|
||||||
private readonly IUiBuilder _uiBuilder;
|
private readonly IUiBuilder _uiBuilder;
|
||||||
private readonly WindowSystem _windowSystem;
|
private readonly WindowSystem _windowSystem;
|
||||||
private readonly FileDialogService _fileDialog;
|
private readonly FileDialogService _fileDialog;
|
||||||
public readonly ConfigWindow Window;
|
private readonly TextureArraySlicer _textureArraySlicer;
|
||||||
public readonly PenumbraChangelog Changelog;
|
public readonly ConfigWindow Window;
|
||||||
public readonly KnowledgeWindow KnowledgeWindow;
|
public readonly PenumbraChangelog Changelog;
|
||||||
|
public readonly KnowledgeWindow KnowledgeWindow;
|
||||||
|
|
||||||
public PenumbraWindowSystem(IDalamudPluginInterface pi, Configuration config, PenumbraChangelog changelog, ConfigWindow window,
|
public PenumbraWindowSystem(IDalamudPluginInterface pi, Configuration config, PenumbraChangelog changelog, ConfigWindow window,
|
||||||
LaunchButton _, ModEditWindow editWindow, FileDialogService fileDialog, ImportPopup importPopup, DebugTab debugTab,
|
LaunchButton _, ModEditWindow editWindow, FileDialogService fileDialog, ImportPopup importPopup, DebugTab debugTab,
|
||||||
KnowledgeWindow knowledgeWindow)
|
KnowledgeWindow knowledgeWindow, TextureArraySlicer textureArraySlicer)
|
||||||
{
|
{
|
||||||
_uiBuilder = pi.UiBuilder;
|
_uiBuilder = pi.UiBuilder;
|
||||||
_fileDialog = fileDialog;
|
_fileDialog = fileDialog;
|
||||||
KnowledgeWindow = knowledgeWindow;
|
_textureArraySlicer = textureArraySlicer;
|
||||||
Changelog = changelog;
|
KnowledgeWindow = knowledgeWindow;
|
||||||
Window = window;
|
Changelog = changelog;
|
||||||
_windowSystem = new WindowSystem("Penumbra");
|
Window = window;
|
||||||
|
_windowSystem = new WindowSystem("Penumbra");
|
||||||
_windowSystem.AddWindow(changelog.Changelog);
|
_windowSystem.AddWindow(changelog.Changelog);
|
||||||
_windowSystem.AddWindow(window);
|
_windowSystem.AddWindow(window);
|
||||||
_windowSystem.AddWindow(editWindow);
|
_windowSystem.AddWindow(editWindow);
|
||||||
|
|
@ -37,6 +40,7 @@ public class PenumbraWindowSystem : IDisposable, IUiService
|
||||||
_uiBuilder.OpenConfigUi += Window.OpenSettings;
|
_uiBuilder.OpenConfigUi += Window.OpenSettings;
|
||||||
_uiBuilder.Draw += _windowSystem.Draw;
|
_uiBuilder.Draw += _windowSystem.Draw;
|
||||||
_uiBuilder.Draw += _fileDialog.Draw;
|
_uiBuilder.Draw += _fileDialog.Draw;
|
||||||
|
_uiBuilder.Draw += _textureArraySlicer.Tick;
|
||||||
_uiBuilder.DisableGposeUiHide = !config.HideUiInGPose;
|
_uiBuilder.DisableGposeUiHide = !config.HideUiInGPose;
|
||||||
_uiBuilder.DisableCutsceneUiHide = !config.HideUiInCutscenes;
|
_uiBuilder.DisableCutsceneUiHide = !config.HideUiInCutscenes;
|
||||||
_uiBuilder.DisableUserUiHide = !config.HideUiWhenUiHidden;
|
_uiBuilder.DisableUserUiHide = !config.HideUiWhenUiHidden;
|
||||||
|
|
@ -51,5 +55,6 @@ public class PenumbraWindowSystem : IDisposable, IUiService
|
||||||
_uiBuilder.OpenConfigUi -= Window.OpenSettings;
|
_uiBuilder.OpenConfigUi -= Window.OpenSettings;
|
||||||
_uiBuilder.Draw -= _windowSystem.Draw;
|
_uiBuilder.Draw -= _windowSystem.Draw;
|
||||||
_uiBuilder.Draw -= _fileDialog.Draw;
|
_uiBuilder.Draw -= _fileDialog.Draw;
|
||||||
|
_uiBuilder.Draw -= _textureArraySlicer.Tick;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue