From fd645485ae19d96922125c4975602a086b1210f4 Mon Sep 17 00:00:00 2001 From: Passive <20432486+PassiveModding@users.noreply.github.com> Date: Tue, 9 Jul 2024 19:27:13 +1000 Subject: [PATCH] Move bulk of parse work to task, keeping gpu stuff on main thread --- Meddle/Meddle.Plugin/UI/CharacterTab.cs | 102 ++++++++++++------------ Meddle/Meddle.Plugin/Utils/ParseUtil.cs | 31 ++++++- 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/Meddle/Meddle.Plugin/UI/CharacterTab.cs b/Meddle/Meddle.Plugin/UI/CharacterTab.cs index c970c49..b65750b 100644 --- a/Meddle/Meddle.Plugin/UI/CharacterTab.cs +++ b/Meddle/Meddle.Plugin/UI/CharacterTab.cs @@ -124,13 +124,13 @@ private void DrawObjectPicker() { if (characterGroup == null) { - ParseCharacter(SelectedCharacter); + exportTask = ParseCharacter(SelectedCharacter); } else { if (ImGui.Button("Parse")) { - ParseCharacter(SelectedCharacter); + exportTask = ParseCharacter(SelectedCharacter); } } } @@ -451,7 +451,7 @@ private List ParseSkeletons(Human* human) return skeletons; } - private Model.MdlGroup? HandleModelPtr(CharacterBase* characterBase, int modelIdx) + private Model.MdlGroup? HandleModelPtr(CharacterBase* characterBase, int modelIdx, Dictionary colorTables) { var modelPtr = characterBase->ModelsSpan[modelIdx]; if (modelPtr == null) return null; @@ -513,22 +513,10 @@ private List ParseSkeletons(Human* human) var cts = ColorTable.Load(ref reader); mtrlFile.ColorTable = cts; } - - var colorTableTex = characterBase->ColorTableTexturesSpan[(modelIdx * CharacterBase.MaxMaterialCount) + j]; - if (colorTableTex != null) + + if (colorTables.TryGetValue((modelIdx * CharacterBase.MaxMaterialCount) + j, out var gpuColorTable)) { - var colorTableTexture = colorTableTex.Value; - if (colorTableTexture != null) - { - log.Debug($"Parsing Color table texture for {mtrlFileName}"); - var textures = ParseUtil.ParseColorTableTexture(colorTableTexture).AsSpan(); - var colorTableBytes = MemoryMarshal.AsBytes(textures); - var colorTableBuf = new byte[colorTableBytes.Length]; - colorTableBytes.CopyTo(colorTableBuf); - var reader = new SpanBinaryReader(colorTableBuf); - var cts = ColorTable.Load(ref reader); - mtrlFile.ColorTable = cts; - } + mtrlFile.ColorTable = gpuColorTable; } var shpkFileResource = dataManager.GameData.GetFile($"shader/sm5/shpk/{shader}"); @@ -561,14 +549,14 @@ private List ParseSkeletons(Human* human) return new Model.MdlGroup(mdlFileName, mdlFile, mtrlGroups.ToArray(), shapeAttributeGroup); } - private ExportUtil.AttachedModelGroup HandleAttachGroup(CharacterBase* attachBase) + private ExportUtil.AttachedModelGroup HandleAttachGroup(CharacterBase* attachBase, Dictionary colorTables) { var attach = new Attach(attachBase->Attach); var models = new List(); var skeleton = new Skeleton.Skeleton(attachBase->Skeleton); for (var i = 0; i < attachBase->ModelsSpan.Length; i++) { - var mdlGroup = HandleModelPtr(attachBase, i); + var mdlGroup = HandleModelPtr(attachBase, i, colorTables); if (mdlGroup != null) { models.Add(mdlGroup); @@ -579,13 +567,18 @@ private ExportUtil.AttachedModelGroup HandleAttachGroup(CharacterBase* attachBas return attachGroup; } - private void ParseCharacterInternal(ICharacter character) + private Task ParseCharacter(ICharacter character) { + if (!exportTask?.IsCompleted ?? false) + { + return exportTask; + } + var charPtr = (CSCharacter*)character.Address; var drawObject = charPtr->GameObject.DrawObject; if (drawObject == null) { - return; + return Task.CompletedTask; } var objectType = drawObject->Object.GetObjectType(); @@ -627,27 +620,17 @@ private void ParseCharacterInternal(ICharacter character) genderRace = GenderRace.Unknown; } - var skeleton = new Skeleton.Skeleton(characterBase->Skeleton); - var mdlGroups = new List(); - for (var i = 0; i < characterBase->SlotCount; i++) - { - var mdlGroup = HandleModelPtr(characterBase, i); - if (mdlGroup != null) - { - mdlGroups.Add(mdlGroup); - } - } + var colorTableTextures = ParseUtil.ParseColorTableTextures(characterBase); - var attachGroups = new List(); - // TODO: Mount/ornament/weapon + var attachDict = new Dictionary, Dictionary>(); if (charPtr->Mount.MountObject != null) { var mountDrawObject = charPtr->Mount.MountObject->GameObject.DrawObject; if (mountDrawObject != null && mountDrawObject->Object.GetObjectType() == ObjectType.CharacterBase) { var mountBase = (CharacterBase*)mountDrawObject; - var attachGroup = HandleAttachGroup(mountBase); - attachGroups.Add(attachGroup); + var mountColorTableTextures = ParseUtil.ParseColorTableTextures(mountBase); + attachDict[mountBase] = mountColorTableTextures; } } @@ -657,8 +640,8 @@ private void ParseCharacterInternal(ICharacter character) if (ornamentDrawObject != null && ornamentDrawObject->Object.GetObjectType() == ObjectType.CharacterBase) { var ornamentBase = (CharacterBase*)ornamentDrawObject; - var attachGroup = HandleAttachGroup(ornamentBase); - attachGroups.Add(attachGroup); + var ornamentColorTableTextures = ParseUtil.ParseColorTableTextures(ornamentBase); + attachDict[ornamentBase] = ornamentColorTableTextures; } } @@ -679,23 +662,40 @@ private void ParseCharacterInternal(ICharacter character) } var weaponBase = (CharacterBase*)draw; - var attachGroup = HandleAttachGroup(weaponBase); - attachGroups.Add(attachGroup); + var weaponColorTableTextures = ParseUtil.ParseColorTableTextures(weaponBase); + attachDict[weaponBase] = weaponColorTableTextures; } } + + // begin background work + return Task.Run(() => + { + var skeleton = new Skeleton.Skeleton(characterBase->Skeleton); + var mdlGroups = new List(); + for (var i = 0; i < characterBase->SlotCount; i++) + { + var mdlGroup = HandleModelPtr(characterBase, i, colorTableTextures); + if (mdlGroup != null) + { + mdlGroups.Add(mdlGroup); + } + } - characterGroup = new ExportUtil.CharacterGroup( - customizeParams, - customizeData, - genderRace, - mdlGroups.ToArray(), - skeleton, - attachGroups.ToArray()); - } + var attachGroups = new List(); + foreach (var (attachBase, attachColorTableTextures) in attachDict) + { + var attachGroup = HandleAttachGroup(attachBase, attachColorTableTextures); + attachGroups.Add(attachGroup); + } - private void ParseCharacter(ICharacter character) - { - ParseCharacterInternal(character); + characterGroup = new ExportUtil.CharacterGroup( + customizeParams, + customizeData, + genderRace, + mdlGroups.ToArray(), + skeleton, + attachGroups.ToArray()); + }); } private void DrawMdlGroup(Model.MdlGroup mdlGroup) diff --git a/Meddle/Meddle.Plugin/Utils/ParseUtil.cs b/Meddle/Meddle.Plugin/Utils/ParseUtil.cs index 8b74fee..c995ed0 100644 --- a/Meddle/Meddle.Plugin/Utils/ParseUtil.cs +++ b/Meddle/Meddle.Plugin/Utils/ParseUtil.cs @@ -1,14 +1,41 @@ -using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; +using System.Runtime.InteropServices; +using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle; using Meddle.Utils; using Meddle.Utils.Files; +using Meddle.Utils.Files.Structs.Material; namespace Meddle.Plugin.Utils; public class ParseUtil { + public static unsafe Dictionary ParseColorTableTextures(CharacterBase* characterBase) + { + var colorTableTextures = new Dictionary(); + for (var i = 0; i < characterBase->ColorTableTexturesSpan.Length; i++) + { + var colorTableTex = characterBase->ColorTableTexturesSpan[i]; + //var colorTableTex = characterBase->ColorTableTexturesSpan[(modelIdx * CharacterBase.MaxMaterialCount) + j]; + if (colorTableTex == null) continue; + + var colorTableTexture = colorTableTex.Value; + if (colorTableTexture != null) + { + var textures = ParseUtil.ParseColorTableTexture(colorTableTexture).AsSpan(); + var colorTableBytes = MemoryMarshal.AsBytes(textures); + var colorTableBuf = new byte[colorTableBytes.Length]; + colorTableBytes.CopyTo(colorTableBuf); + var reader = new SpanBinaryReader(colorTableBuf); + var cts = ColorTable.Load(ref reader); + colorTableTextures[i] = cts; + } + } + + return colorTableTextures; + } - // Only call from UI thread or you will probably crash + // Only call from main thread or you will probably crash public static unsafe MaterialResourceHandle.ColorTableRow[] ParseColorTableTexture(Texture* colorTableTexture) { var (colorTableRes, stride) = DXHelper.ExportTextureResource(colorTableTexture);