Skip to content

Commit

Permalink
Move bulk of parse work to task, keeping gpu stuff on main thread
Browse files Browse the repository at this point in the history
  • Loading branch information
PassiveModding committed Jul 9, 2024
1 parent 2a6576e commit fd64548
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 53 deletions.
102 changes: 51 additions & 51 deletions Meddle/Meddle.Plugin/UI/CharacterTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -451,7 +451,7 @@ private List<HavokXml> ParseSkeletons(Human* human)
return skeletons;
}

private Model.MdlGroup? HandleModelPtr(CharacterBase* characterBase, int modelIdx)
private Model.MdlGroup? HandleModelPtr(CharacterBase* characterBase, int modelIdx, Dictionary<int, ColorTable> colorTables)
{
var modelPtr = characterBase->ModelsSpan[modelIdx];
if (modelPtr == null) return null;
Expand Down Expand Up @@ -513,22 +513,10 @@ private List<HavokXml> 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}");
Expand Down Expand Up @@ -561,14 +549,14 @@ private List<HavokXml> 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<int, ColorTable> colorTables)
{
var attach = new Attach(attachBase->Attach);
var models = new List<Model.MdlGroup>();
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);
Expand All @@ -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;

Check warning on line 574 in Meddle/Meddle.Plugin/UI/CharacterTab.cs

View workflow job for this annotation

GitHub Actions / Build (7.0.x, latest)

Possible null reference return.

Check warning on line 574 in Meddle/Meddle.Plugin/UI/CharacterTab.cs

View workflow job for this annotation

GitHub Actions / Build (7.0.x, latest)

Possible null reference return.

Check warning on line 574 in Meddle/Meddle.Plugin/UI/CharacterTab.cs

View workflow job for this annotation

GitHub Actions / Release

Possible null reference return.
}

var charPtr = (CSCharacter*)character.Address;
var drawObject = charPtr->GameObject.DrawObject;
if (drawObject == null)
{
return;
return Task.CompletedTask;
}

var objectType = drawObject->Object.GetObjectType();
Expand Down Expand Up @@ -627,27 +620,17 @@ private void ParseCharacterInternal(ICharacter character)
genderRace = GenderRace.Unknown;
}

var skeleton = new Skeleton.Skeleton(characterBase->Skeleton);
var mdlGroups = new List<Model.MdlGroup>();
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<ExportUtil.AttachedModelGroup>();
// TODO: Mount/ornament/weapon
var attachDict = new Dictionary<Pointer<CharacterBase>, Dictionary<int, ColorTable>>();
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;
}
}

Expand All @@ -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;
}
}

Expand All @@ -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<Model.MdlGroup>();
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<ExportUtil.AttachedModelGroup>();
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)
Expand Down
31 changes: 29 additions & 2 deletions Meddle/Meddle.Plugin/Utils/ParseUtil.cs
Original file line number Diff line number Diff line change
@@ -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<int, ColorTable> ParseColorTableTextures(CharacterBase* characterBase)
{
var colorTableTextures = new Dictionary<int, ColorTable>();
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);
Expand Down

0 comments on commit fd64548

Please sign in to comment.