mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Improve texture saving
This commit is contained in:
parent
93b0996794
commit
e5620e17e0
5 changed files with 50 additions and 20 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Textures;
|
using Dalamud.Interface.Textures;
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
using Dalamud.Interface.Textures.TextureWraps;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
|
@ -6,15 +7,17 @@ using OtterGui.Log;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using OtterGui.Tasks;
|
using OtterGui.Tasks;
|
||||||
using OtterTex;
|
using OtterTex;
|
||||||
|
using SharpDX.Direct3D11;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Formats.Png;
|
using SixLabors.ImageSharp.Formats.Png;
|
||||||
using SixLabors.ImageSharp.Formats.Tga;
|
using SixLabors.ImageSharp.Formats.Tga;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using DxgiDevice = SharpDX.DXGI.Device;
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
|
|
||||||
namespace Penumbra.Import.Textures;
|
namespace Penumbra.Import.Textures;
|
||||||
|
|
||||||
public sealed class TextureManager(IDataManager gameData, Logger logger, ITextureProvider textureProvider)
|
public sealed class TextureManager(IDataManager gameData, Logger logger, ITextureProvider textureProvider, IUiBuilder uiBuilder)
|
||||||
: SingleTaskQueue, IDisposable, IService
|
: SingleTaskQueue, IDisposable, IService
|
||||||
{
|
{
|
||||||
private readonly Logger _logger = logger;
|
private readonly Logger _logger = logger;
|
||||||
|
|
@ -47,11 +50,11 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur
|
||||||
|
|
||||||
|
|
||||||
public Task SaveAs(CombinedTexture.TextureSaveType type, bool mipMaps, bool asTex, string input, string output)
|
public Task SaveAs(CombinedTexture.TextureSaveType type, bool mipMaps, bool asTex, string input, string output)
|
||||||
=> Enqueue(new SaveAsAction(this, type, mipMaps, asTex, input, output));
|
=> Enqueue(new SaveAsAction(this, type, uiBuilder.Device, mipMaps, asTex, input, output));
|
||||||
|
|
||||||
public Task SaveAs(CombinedTexture.TextureSaveType type, bool mipMaps, bool asTex, BaseImage image, string path, byte[]? rgba = null,
|
public Task SaveAs(CombinedTexture.TextureSaveType type, bool mipMaps, bool asTex, BaseImage image, string path, byte[]? rgba = null,
|
||||||
int width = 0, int height = 0)
|
int width = 0, int height = 0)
|
||||||
=> Enqueue(new SaveAsAction(this, type, mipMaps, asTex, image, path, rgba, width, height));
|
=> Enqueue(new SaveAsAction(this, type, uiBuilder.Device, mipMaps, asTex, image, path, rgba, width, height));
|
||||||
|
|
||||||
private Task Enqueue(IAction action)
|
private Task Enqueue(IAction action)
|
||||||
{
|
{
|
||||||
|
|
@ -156,27 +159,30 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur
|
||||||
private readonly string _outputPath;
|
private readonly string _outputPath;
|
||||||
private readonly ImageInputData _input;
|
private readonly ImageInputData _input;
|
||||||
private readonly CombinedTexture.TextureSaveType _type;
|
private readonly CombinedTexture.TextureSaveType _type;
|
||||||
|
private readonly Device? _device;
|
||||||
private readonly bool _mipMaps;
|
private readonly bool _mipMaps;
|
||||||
private readonly bool _asTex;
|
private readonly bool _asTex;
|
||||||
|
|
||||||
public SaveAsAction(TextureManager textures, CombinedTexture.TextureSaveType type, bool mipMaps, bool asTex, string input,
|
public SaveAsAction(TextureManager textures, CombinedTexture.TextureSaveType type, Device? device, bool mipMaps, bool asTex,
|
||||||
string output)
|
string input, string output)
|
||||||
{
|
{
|
||||||
_textures = textures;
|
_textures = textures;
|
||||||
_input = new ImageInputData(input);
|
_input = new ImageInputData(input);
|
||||||
_outputPath = output;
|
_outputPath = output;
|
||||||
_type = type;
|
_type = type;
|
||||||
|
_device = device;
|
||||||
_mipMaps = mipMaps;
|
_mipMaps = mipMaps;
|
||||||
_asTex = asTex;
|
_asTex = asTex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaveAsAction(TextureManager textures, CombinedTexture.TextureSaveType type, bool mipMaps, bool asTex, BaseImage image,
|
public SaveAsAction(TextureManager textures, CombinedTexture.TextureSaveType type, Device? device, bool mipMaps, bool asTex,
|
||||||
string path, byte[]? rgba = null, int width = 0, int height = 0)
|
BaseImage image, string path, byte[]? rgba = null, int width = 0, int height = 0)
|
||||||
{
|
{
|
||||||
_textures = textures;
|
_textures = textures;
|
||||||
_input = new ImageInputData(image, rgba, width, height);
|
_input = new ImageInputData(image, rgba, width, height);
|
||||||
_outputPath = path;
|
_outputPath = path;
|
||||||
_type = type;
|
_type = type;
|
||||||
|
_device = device;
|
||||||
_mipMaps = mipMaps;
|
_mipMaps = mipMaps;
|
||||||
_asTex = asTex;
|
_asTex = asTex;
|
||||||
}
|
}
|
||||||
|
|
@ -201,8 +207,8 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur
|
||||||
rgba, width, height),
|
rgba, width, height),
|
||||||
CombinedTexture.TextureSaveType.AsIs when imageTypeBehaviour is TextureType.Dds => AddMipMaps(image.AsDds!, _mipMaps),
|
CombinedTexture.TextureSaveType.AsIs when imageTypeBehaviour is TextureType.Dds => AddMipMaps(image.AsDds!, _mipMaps),
|
||||||
CombinedTexture.TextureSaveType.Bitmap => ConvertToRgbaDds(image, _mipMaps, cancel, rgba, width, height),
|
CombinedTexture.TextureSaveType.Bitmap => ConvertToRgbaDds(image, _mipMaps, cancel, rgba, width, height),
|
||||||
CombinedTexture.TextureSaveType.BC3 => ConvertToCompressedDds(image, _mipMaps, false, cancel, rgba, width, height),
|
CombinedTexture.TextureSaveType.BC3 => ConvertToCompressedDds(image, _mipMaps, false, _device, cancel, rgba, width, height),
|
||||||
CombinedTexture.TextureSaveType.BC7 => ConvertToCompressedDds(image, _mipMaps, true, cancel, rgba, width, height),
|
CombinedTexture.TextureSaveType.BC7 => ConvertToCompressedDds(image, _mipMaps, true, _device, cancel, rgba, width, height),
|
||||||
_ => throw new Exception("Wrong save type."),
|
_ => throw new Exception("Wrong save type."),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -320,8 +326,8 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Convert an existing image to a block compressed .dds. Does not create a deep copy of an existing dds of the correct format and just returns the existing one. </summary>
|
/// <summary> Convert an existing image to a block compressed .dds. Does not create a deep copy of an existing dds of the correct format and just returns the existing one. </summary>
|
||||||
public static BaseImage ConvertToCompressedDds(BaseImage input, bool mipMaps, bool bc7, CancellationToken cancel, byte[]? rgba = null,
|
public static BaseImage ConvertToCompressedDds(BaseImage input, bool mipMaps, bool bc7, Device? device, CancellationToken cancel,
|
||||||
int width = 0, int height = 0)
|
byte[]? rgba = null, int width = 0, int height = 0)
|
||||||
{
|
{
|
||||||
switch (input.Type.ReduceToBehaviour())
|
switch (input.Type.ReduceToBehaviour())
|
||||||
{
|
{
|
||||||
|
|
@ -331,12 +337,12 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur
|
||||||
cancel.ThrowIfCancellationRequested();
|
cancel.ThrowIfCancellationRequested();
|
||||||
var dds = ConvertToDds(rgba, width, height).AsDds!;
|
var dds = ConvertToDds(rgba, width, height).AsDds!;
|
||||||
cancel.ThrowIfCancellationRequested();
|
cancel.ThrowIfCancellationRequested();
|
||||||
return CreateCompressed(dds, mipMaps, bc7, cancel);
|
return CreateCompressed(dds, mipMaps, bc7, device, cancel);
|
||||||
}
|
}
|
||||||
case TextureType.Dds:
|
case TextureType.Dds:
|
||||||
{
|
{
|
||||||
var scratch = input.AsDds!;
|
var scratch = input.AsDds!;
|
||||||
return CreateCompressed(scratch, mipMaps, bc7, cancel);
|
return CreateCompressed(scratch, mipMaps, bc7, device, cancel);
|
||||||
}
|
}
|
||||||
default: return new BaseImage();
|
default: return new BaseImage();
|
||||||
}
|
}
|
||||||
|
|
@ -384,7 +390,7 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Create a BC3 or BC7 block-compressed .dds from the input (optionally with mipmaps). Returns input (+ mipmaps) if it is already the correct format. </summary>
|
/// <summary> Create a BC3 or BC7 block-compressed .dds from the input (optionally with mipmaps). Returns input (+ mipmaps) if it is already the correct format. </summary>
|
||||||
public static ScratchImage CreateCompressed(ScratchImage input, bool mipMaps, bool bc7, CancellationToken cancel)
|
public static ScratchImage CreateCompressed(ScratchImage input, bool mipMaps, bool bc7, Device? device, CancellationToken cancel)
|
||||||
{
|
{
|
||||||
var format = bc7 ? DXGIFormat.BC7UNorm : DXGIFormat.BC3UNorm;
|
var format = bc7 ? DXGIFormat.BC7UNorm : DXGIFormat.BC3UNorm;
|
||||||
if (input.Meta.Format == format)
|
if (input.Meta.Format == format)
|
||||||
|
|
@ -398,6 +404,15 @@ public sealed class TextureManager(IDataManager gameData, Logger logger, ITextur
|
||||||
|
|
||||||
input = AddMipMaps(input, mipMaps);
|
input = AddMipMaps(input, mipMaps);
|
||||||
cancel.ThrowIfCancellationRequested();
|
cancel.ThrowIfCancellationRequested();
|
||||||
|
// See https://github.com/microsoft/DirectXTex/wiki/Compress#parameters for the format condition.
|
||||||
|
if (device is not null && format is DXGIFormat.BC6HUF16 or DXGIFormat.BC6HSF16 or DXGIFormat.BC7UNorm or DXGIFormat.BC7UNormSRGB)
|
||||||
|
{
|
||||||
|
var dxgiDevice = device.QueryInterface<DxgiDevice>();
|
||||||
|
|
||||||
|
using var deviceClone = new Device(dxgiDevice.Adapter, device.CreationFlags, device.FeatureLevel);
|
||||||
|
return input.Compress(deviceClone.NativePointer, format, CompressFlags.Parallel);
|
||||||
|
}
|
||||||
|
|
||||||
return input.Compress(format, CompressFlags.BC7Quick | CompressFlags.Parallel);
|
return input.Compress(format, CompressFlags.BC7Quick | CompressFlags.Parallel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,10 @@
|
||||||
<HintPath>$(DalamudLibPath)SharpDX.Direct3D11.dll</HintPath>
|
<HintPath>$(DalamudLibPath)SharpDX.Direct3D11.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="SharpDX.DXGI">
|
||||||
|
<HintPath>$(DalamudLibPath)SharpDX.DXGI.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>
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ public partial class MtrlTab
|
||||||
foreach (var constant in Mtrl.ShaderPackage.Constants)
|
foreach (var constant in Mtrl.ShaderPackage.Constants)
|
||||||
{
|
{
|
||||||
var values = Mtrl.GetConstantValue<byte>(constant);
|
var values = Mtrl.GetConstantValue<byte>(constant);
|
||||||
if (values != null)
|
if (values != [])
|
||||||
SetMaterialParameter(constant.Id, 0, values);
|
SetMaterialParameter(constant.Id, 0, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ public partial class ModEditWindow
|
||||||
tt, !isActive || !canSaveInPlace || _center.IsLeftCopy && _currentSaveAs == (int)CombinedTexture.TextureSaveType.AsIs))
|
tt, !isActive || !canSaveInPlace || _center.IsLeftCopy && _currentSaveAs == (int)CombinedTexture.TextureSaveType.AsIs))
|
||||||
{
|
{
|
||||||
_center.SaveAs(_left.Type, _textures, _left.Path, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
|
_center.SaveAs(_left.Type, _textures, _left.Path, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
|
||||||
InvokeChange(Mod, _left.Path);
|
AddChangeTask(_left.Path);
|
||||||
AddReloadTask(_left.Path, false);
|
AddReloadTask(_left.Path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,7 +159,7 @@ public partial class ModEditWindow
|
||||||
!canConvertInPlace || _left.Format is DXGIFormat.BC7Typeless or DXGIFormat.BC7UNorm or DXGIFormat.BC7UNormSRGB))
|
!canConvertInPlace || _left.Format is DXGIFormat.BC7Typeless or DXGIFormat.BC7UNorm or DXGIFormat.BC7UNormSRGB))
|
||||||
{
|
{
|
||||||
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC7, _left.MipMaps > 1);
|
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC7, _left.MipMaps > 1);
|
||||||
InvokeChange(Mod, _left.Path);
|
AddChangeTask(_left.Path);
|
||||||
AddReloadTask(_left.Path, false);
|
AddReloadTask(_left.Path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,7 +169,7 @@ public partial class ModEditWindow
|
||||||
!canConvertInPlace || _left.Format is DXGIFormat.BC3Typeless or DXGIFormat.BC3UNorm or DXGIFormat.BC3UNormSRGB))
|
!canConvertInPlace || _left.Format is DXGIFormat.BC3Typeless or DXGIFormat.BC3UNorm or DXGIFormat.BC3UNormSRGB))
|
||||||
{
|
{
|
||||||
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC3, _left.MipMaps > 1);
|
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.BC3, _left.MipMaps > 1);
|
||||||
InvokeChange(Mod, _left.Path);
|
AddChangeTask(_left.Path);
|
||||||
AddReloadTask(_left.Path, false);
|
AddReloadTask(_left.Path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +180,7 @@ public partial class ModEditWindow
|
||||||
|| _left.Format is DXGIFormat.B8G8R8A8UNorm or DXGIFormat.B8G8R8A8Typeless or DXGIFormat.B8G8R8A8UNormSRGB))
|
|| _left.Format is DXGIFormat.B8G8R8A8UNorm or DXGIFormat.B8G8R8A8Typeless or DXGIFormat.B8G8R8A8UNormSRGB))
|
||||||
{
|
{
|
||||||
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.Bitmap, _left.MipMaps > 1);
|
_center.SaveAsTex(_textures, _left.Path, CombinedTexture.TextureSaveType.Bitmap, _left.MipMaps > 1);
|
||||||
InvokeChange(Mod, _left.Path);
|
AddChangeTask(_left.Path);
|
||||||
AddReloadTask(_left.Path, false);
|
AddReloadTask(_left.Path, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -235,7 +235,7 @@ public partial class ModEditWindow
|
||||||
if (a)
|
if (a)
|
||||||
{
|
{
|
||||||
_center.SaveAs(null, _textures, b, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
|
_center.SaveAs(null, _textures, b, (CombinedTexture.TextureSaveType)_currentSaveAs, _addMipMaps);
|
||||||
InvokeChange(Mod, b);
|
AddChangeTask(b);
|
||||||
if (b == _left.Path)
|
if (b == _left.Path)
|
||||||
AddReloadTask(_left.Path, false);
|
AddReloadTask(_left.Path, false);
|
||||||
else if (b == _right.Path)
|
else if (b == _right.Path)
|
||||||
|
|
@ -245,6 +245,17 @@ public partial class ModEditWindow
|
||||||
_forceTextureStartPath = false;
|
_forceTextureStartPath = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddChangeTask(string path)
|
||||||
|
{
|
||||||
|
_center.SaveTask.ContinueWith(t =>
|
||||||
|
{
|
||||||
|
if (!t.IsCompletedSuccessfully)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_framework.RunOnFrameworkThread(() => InvokeChange(Mod, path));
|
||||||
|
}, TaskScheduler.Default);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddReloadTask(string path, bool right)
|
private void AddReloadTask(string path, bool right)
|
||||||
{
|
{
|
||||||
_center.SaveTask.ContinueWith(t =>
|
_center.SaveTask.ContinueWith(t =>
|
||||||
|
|
|
||||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue