Skip to content

Commit cfc4b31

Browse files
committed
Re-add BC7
1 parent 482f053 commit cfc4b31

1 file changed

Lines changed: 156 additions & 6 deletions

File tree

Snowcloak/UI/DataAnalysisUi.cs

Lines changed: 156 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using ElezenTools.UI;
66
using Snowcloak.API.Data.Enum;
77
using Microsoft.Extensions.Logging;
8+
using Penumbra.Api.Enums;
9+
using Snowcloak.Interop.Ipc;
810
using Snowcloak.Services;
911
using Snowcloak.Services.Mediator;
1012
using Snowcloak.Utils;
@@ -15,22 +17,30 @@ namespace Snowcloak.UI;
1517
public class DataAnalysisUi : WindowMediatorSubscriberBase
1618
{
1719
private readonly CharacterAnalyzer _characterAnalyzer;
18-
private readonly UiSharedService _uiSharedService;
20+
private readonly Progress<(string, int)> _conversionProgress = new();
21+
private readonly IpcManager _ipcManager;
22+
private readonly Dictionary<string, (TextureType TextureType, string[] Duplicates)> _texturesToConvert = new(StringComparer.Ordinal);
1923
private Dictionary<ObjectKind, Dictionary<string, CharacterAnalyzer.FileDataEntry>>? _cachedAnalysis;
24+
private CancellationTokenSource _conversionCancellationTokenSource = new();
25+
private string _conversionCurrentFileName = string.Empty;
26+
private int _conversionCurrentFileProgress;
27+
private Task? _conversionTask;
28+
private bool _enableTextureCompressionMode;
2029
private bool _hasUpdate = false;
30+
private bool _modalOpen;
2131
private bool _sortDirty = true;
2232
private string _selectedFileTypeTab = string.Empty;
2333
private string _selectedHash = string.Empty;
2434
private ObjectKind _selectedObjectTab;
35+
private bool _showModal;
2536

2637
public DataAnalysisUi(ILogger<DataAnalysisUi> logger, SnowMediator mediator,
27-
CharacterAnalyzer characterAnalyzer,
28-
PerformanceCollectorService performanceCollectorService,
29-
UiSharedService uiSharedService)
38+
CharacterAnalyzer characterAnalyzer, IpcManager ipcManager,
39+
PerformanceCollectorService performanceCollectorService)
3040
: base(logger, mediator, "Snowcloak Character Data Analysis###SnowcloakDataAnalysisUI", performanceCollectorService)
3141
{
3242
_characterAnalyzer = characterAnalyzer;
33-
_uiSharedService = uiSharedService;
43+
_ipcManager = ipcManager;
3444
WindowName = "Character Data Analysis";
3545
Mediator.Subscribe<CharacterDataAnalyzedMessage>(this, (_) =>
3646
{
@@ -49,10 +59,47 @@ public DataAnalysisUi(ILogger<DataAnalysisUi> logger, SnowMediator mediator,
4959
Y = 2160
5060
}
5161
};
62+
63+
_conversionProgress.ProgressChanged += ConversionProgress_ProgressChanged;
5264
}
5365

5466
protected override void DrawInternal()
5567
{
68+
const string conversionPopupTitle = "Texture Compression in Progress";
69+
if (_conversionTask != null && !_conversionTask.IsCompleted)
70+
{
71+
_showModal = true;
72+
if (ImGui.BeginPopupModal(conversionPopupTitle))
73+
{
74+
ImGui.TextUnformatted(string.Format("Texture compression in progress: {0}/{1}", _conversionCurrentFileProgress, _texturesToConvert.Count));
75+
ImGui.TextWrapped(string.Format("Current file: {0}", _conversionCurrentFileName));
76+
if (ElezenImgui.ShowIconButton(FontAwesomeIcon.StopCircle, "Cancel compression"))
77+
{
78+
_conversionCancellationTokenSource.Cancel();
79+
}
80+
UiSharedService.SetScaledWindowSize(500);
81+
ImGui.EndPopup();
82+
}
83+
else
84+
{
85+
_modalOpen = false;
86+
}
87+
}
88+
else if (_conversionTask != null && _conversionTask.IsCompleted && _texturesToConvert.Count > 0)
89+
{
90+
_conversionTask = null;
91+
_texturesToConvert.Clear();
92+
_showModal = false;
93+
_modalOpen = false;
94+
_enableTextureCompressionMode = false;
95+
}
96+
97+
if (_showModal && !_modalOpen)
98+
{
99+
ImGui.OpenPopup(conversionPopupTitle);
100+
_modalOpen = true;
101+
}
102+
56103
if (_hasUpdate)
57104
{
58105
_cachedAnalysis = _characterAnalyzer.LastAnalysis.DeepClone();
@@ -212,6 +259,8 @@ protected override void DrawInternal()
212259
_selectedHash = string.Empty;
213260
_selectedObjectTab = kvp.Key;
214261
_selectedFileTypeTab = string.Empty;
262+
_enableTextureCompressionMode = false;
263+
_texturesToConvert.Clear();
215264
}
216265

217266
using var fileTabBar = ImRaii.TabBar("fileTabs");
@@ -234,6 +283,8 @@ protected override void DrawInternal()
234283
{
235284
_selectedFileTypeTab = fileGroup.Key;
236285
_selectedHash = string.Empty;
286+
_enableTextureCompressionMode = false;
287+
_texturesToConvert.Clear();
237288
}
238289

239290
ImGui.TextUnformatted(string.Format("{0} files", fileGroup.Key));
@@ -248,6 +299,29 @@ protected override void DrawInternal()
248299
ImGui.SameLine();
249300
ImGui.TextUnformatted(UiSharedService.ByteToString(fileGroup.Sum(c => c.CompressedSize)));
250301

302+
if (string.Equals(_selectedFileTypeTab, "tex", StringComparison.Ordinal))
303+
{
304+
ImGui.Checkbox("Enable BC7 compression mode", ref _enableTextureCompressionMode);
305+
if (_enableTextureCompressionMode)
306+
{
307+
ElezenImgui.ColouredText("WARNING REGARDING TEXTURE COMPRESSION:", ImGuiColors.DalamudYellow);
308+
ImGui.SameLine();
309+
ElezenImgui.ColouredText("Converting textures is irreversible!", ImGuiColors.DalamudRed);
310+
ElezenImgui.ColouredWrappedText("- Compressing textures can reduce file size and VRAM use, especially for large textures."
311+
+ Environment.NewLine + "- Selected textures will be converted to BC7."
312+
+ Environment.NewLine + "- Some textures, especially colorsets, normal maps, greyscale maps, and detail maps, can produce visual artifacts after BC7 compression."
313+
+ Environment.NewLine + "- Keep the original mod files available so you can reimport them if the result is wrong."
314+
+ Environment.NewLine + "- Duplicate texture files are converted automatically."
315+
+ Environment.NewLine + "- Texture compression is expensive and can take a while.",
316+
ImGuiColors.DalamudYellow);
317+
if (_texturesToConvert.Count > 0 && ElezenImgui.ShowIconButton(FontAwesomeIcon.PlayCircle, string.Format("Start compression of {0} texture(s)", _texturesToConvert.Count)))
318+
{
319+
_conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate();
320+
_conversionTask = _ipcManager.Penumbra.ConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token);
321+
}
322+
}
323+
}
324+
251325
ImGui.Separator();
252326
DrawTable(fileGroup);
253327
ImGui.EndTabItem();
@@ -297,6 +371,10 @@ protected override void DrawInternal()
297371
ElezenImgui.WrappedText(traits.FormatSummary);
298372
ElezenImgui.WrappedText(string.Format("Channel variance (RGB): {0}/{1}/{2}", traits.RedVariance.ToString("0.0"), traits.GreenVariance.ToString("0.0"), traits.BlueVariance.ToString("0.0")));
299373
ElezenImgui.WrappedText(string.Format("Alpha transitions: {0}", traits.AlphaTransitionDensity.ToString("P1")));
374+
if (IsRiskyConversion(item))
375+
{
376+
ElezenImgui.ColouredWrappedText("Flagged as risky for compression (colorset/dye path, high alpha transitions, or greyscale map).", ImGuiColors.DalamudOrange);
377+
}
300378
}
301379
}
302380
}
@@ -305,17 +383,35 @@ public override void OnOpen()
305383
{
306384
_hasUpdate = true;
307385
_selectedHash = string.Empty;
386+
_enableTextureCompressionMode = false;
387+
_texturesToConvert.Clear();
308388
}
309389

310390
protected override void Dispose(bool disposing)
311391
{
312392
base.Dispose(disposing);
393+
_conversionProgress.ProgressChanged -= ConversionProgress_ProgressChanged;
394+
try
395+
{
396+
_conversionCancellationTokenSource.Cancel();
397+
}
398+
catch (ObjectDisposedException ex)
399+
{
400+
_logger.LogTrace(ex, "Texture compression cancellation token source was already disposed.");
401+
}
402+
_conversionCancellationTokenSource.Dispose();
403+
}
404+
405+
private void ConversionProgress_ProgressChanged(object? sender, (string, int) e)
406+
{
407+
_conversionCurrentFileName = e.Item1;
408+
_conversionCurrentFileProgress = e.Item2;
313409
}
314410

315411
private void DrawTable(IGrouping<string, CharacterAnalyzer.FileDataEntry> fileGroup)
316412
{
317413
var tableColumns = string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal)
318-
? 6
414+
? (_enableTextureCompressionMode ? 7 : 6)
319415
: (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal) ? 6 : 5);
320416
using var table = ImRaii.Table("Analysis", tableColumns, ImGuiTableFlags.Sortable | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
321417
new Vector2(0, 300));
@@ -328,6 +424,10 @@ private void DrawTable(IGrouping<string, CharacterAnalyzer.FileDataEntry> fileGr
328424
if (string.Equals(fileGroup.Key, "tex", StringComparison.Ordinal))
329425
{
330426
ImGui.TableSetupColumn("Format");
427+
if (_enableTextureCompressionMode)
428+
{
429+
ImGui.TableSetupColumn("BC7");
430+
}
331431
}
332432
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal))
333433
{
@@ -404,6 +504,43 @@ private void DrawTable(IGrouping<string, CharacterAnalyzer.FileDataEntry> fileGr
404504
ImGui.TableNextColumn();
405505
ImGui.TextUnformatted(item.Format.Value);
406506
if (ImGui.IsItemClicked()) _selectedHash = item.Hash;
507+
if (_enableTextureCompressionMode)
508+
{
509+
ImGui.TableNextColumn();
510+
if (IsAlreadyBlockCompressed(item))
511+
{
512+
ImGui.TextUnformatted("");
513+
continue;
514+
}
515+
516+
var filePath = item.FilePaths[0];
517+
var toConvert = _texturesToConvert.ContainsKey(filePath);
518+
if (ImGui.Checkbox("###convert" + item.Hash, ref toConvert))
519+
{
520+
if (toConvert)
521+
{
522+
_texturesToConvert[filePath] = (TextureType.Bc7Tex, item.FilePaths.Skip(1).ToArray());
523+
}
524+
else
525+
{
526+
_texturesToConvert.Remove(filePath);
527+
}
528+
}
529+
if (toConvert)
530+
{
531+
_texturesToConvert[filePath] = (TextureType.Bc7Tex, item.FilePaths.Skip(1).ToArray());
532+
}
533+
if (IsRiskyConversion(item))
534+
{
535+
ImGui.SameLine();
536+
ElezenImgui.ShowIcon(FontAwesomeIcon.ExclamationTriangle, ImGuiColors.DalamudOrange);
537+
ElezenImgui.AttachTooltip("Texture flagged as risky for BC7 compression. Proceed with caution.");
538+
if (!toConvert)
539+
{
540+
_texturesToConvert.Remove(filePath);
541+
}
542+
}
543+
}
407544
}
408545
if (string.Equals(fileGroup.Key, "mdl", StringComparison.Ordinal))
409546
{
@@ -413,4 +550,17 @@ private void DrawTable(IGrouping<string, CharacterAnalyzer.FileDataEntry> fileGr
413550
}
414551
}
415552
}
553+
554+
private static bool IsAlreadyBlockCompressed(CharacterAnalyzer.FileDataEntry item)
555+
{
556+
var format = item.Format.Value;
557+
return format.StartsWith("BC", StringComparison.OrdinalIgnoreCase)
558+
|| format.StartsWith("DXT", StringComparison.OrdinalIgnoreCase)
559+
|| format.StartsWith("24864", StringComparison.Ordinal);
560+
}
561+
562+
private static bool IsRiskyConversion(CharacterAnalyzer.FileDataEntry item)
563+
{
564+
return item.IsRiskyTexture;
565+
}
416566
}

0 commit comments

Comments
 (0)