Project-Rito / ProjectRito-Dev

An experimental Breath of the Wild map editor with ambitions of being production-ready.
7 stars 2 forks source link

Memory exception when caching baked collision #32

Closed ArchLeaders closed 1 year ago

ArchLeaders commented 1 year ago

I have a lot of memory (about 64GB), but I get this error when clicking "Cache Baked Collision" on the MubinToolSettings menu.

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at System.IO.BinaryReader.ReadBytes(Int32 count)
   at HKX2.BinaryReaderEx.ReadBytes(Int32 count) in F:\GitHub\ProjectRito-Dev\HKX2Builders\lib\HKX2Library\HKX2\BinaryReaderEx.cs:line 54
   at HKX2.HKXSection..ctor(BinaryReaderEx br) in F:\GitHub\ProjectRito-Dev\HKX2Builders\lib\HKX2Library\HKX2\PackFileCommon.cs:line 360
   at HKX2.PackFileDeserializer.DeserializePartially(BinaryReaderEx br) in F:\GitHub\ProjectRito-Dev\HKX2Builders\lib\HKX2Library\HKX2\PackFileDeserializer.cs:line 51
   at HKX2.PackFileDeserializer.Deserialize(BinaryReaderEx br) in F:\GitHub\ProjectRito-Dev\HKX2Builders\lib\HKX2Library\HKX2\PackFileDeserializer.cs:line 59
   at HKX2.Util.ReadHKX(Stream stream) in F:\GitHub\ProjectRito-Dev\HKX2Builders\lib\HKX2Library\HKX2\Util.cs:line 23
   at HKX2.Util.ReadBotwHKX(Stream stream, String extension) in F:\GitHub\ProjectRito-Dev\HKX2Builders\lib\HKX2Library\HKX2\Util.cs:line 57
   at HKX2.Util.ReadBotwHKX(Byte[] bytes, String extension) in F:\GitHub\ProjectRito-Dev\HKX2Builders\lib\HKX2Library\HKX2\Util.cs:line 67
   at UKingLibrary.MapCollisionLoader.Load(Stream stream, String fileName, GLScene scene) in F:\GitHub\ProjectRito-Dev\Plugins\UKingLibrary\Loading\MapCollisionLoader.cs:line 47
   at UKingLibrary.CollisionCacher.CacheFieldCollision(String cacheDir) in F:\GitHub\ProjectRito-Dev\Plugins\UKingLibrary\Tools\CollisionCacher.cs:line 63
   at UKingLibrary.CollisionCacher.CacheAll(String cacheDir) in F:\GitHub\ProjectRito-Dev\Plugins\UKingLibrary\Tools\CollisionCacher.cs:line 21
   at UKingLibrary.MubinToolSettings.<>c.<Render>b__6_0() in F:\GitHub\ProjectRito-Dev\Plugins\UKingLibrary\UI\MubinToolSettings.cs:line 47

Looks like this might just be an issue with allocating every byml in every field... but, Idk how it works so I can't do much to try and fix it without breaking it.

ArchLeaders commented 1 year ago

Update: at some point it was failing when loading a BYML, but currently it fails after loading all the fields and instead crashes loading the havok objects.

ArchLeaders commented 1 year ago

I tried for a while to fix it, and got pretty close too, but I still eventually ran into a memory exception.

Here's the code that got me closest. I removed all the object storage and just wrote the file as soon as the information was gathered. But based on the debugger it still looks like something isn't being deallocated.

Anything stick out to you here?

public static void CacheFieldCollision(string cacheDir)
{
    Directory.CreateDirectory(cacheDir);
    foreach (string fieldName in GlobalData.FieldNames) {
        foreach (string sectionName in GlobalData.SectionNames) {
            MapCollisionLoader[] collisionLoaders = new MapCollisionLoader[4];
            for (int i = 0; i < 4; i++) {
                string path = PluginConfig.GetContentPath($"Physics/StaticCompound/{fieldName}/{sectionName}-{i}.shksc");
                MapCollisionLoader loader = new();

                using FileStream fs = File.OpenRead(path);
                loader.Load(fs, Path.GetFileName(path));
                collisionLoaders[i] = loader;
            }

            foreach (string muuntEnding in GlobalData.MuuntEndings) {
                string path = PluginConfig.GetContentPath($"Map/{fieldName}/{sectionName}/{sectionName}_{muuntEnding}.smubin");
                using FileStream fs = File.OpenRead(path);
                STFileLoader.Settings mubinSettings = STFileLoader.TryDecompressFile(fs, Path.GetFileName(path));
                if (mubinSettings.Stream == null) {
                    StudioLogger.WriteError($"Trouble reading {path}!");
                    continue;
                }

                BymlFile mubin = BymlFile.FromBinary(mubinSettings.Stream);
                mubinSettings.Stream.Dispose();

                ProcessMapUnit(cacheDir, mubin, collisionLoaders);
            }

            GC.Collect();
        }
    }
}

private static void ProcessMapUnit(string cacheDir, BymlFile mubin, MapCollisionLoader[] collisionLoaders)
{
    List<BymlNode> objects = mubin.RootNode.Hash["Objs"].Array;
    foreach (var obj in objects) {
        string actorName = obj.Hash["UnitConfigName"].String;
        if (!_cached.Contains(actorName)) {
            for (int i = 0; i < 4; i++) {
                MapCollisionLoader collisionLoader = new();
                collisionLoader.CreateForCaching();

                BakedCollisionShapeCacheable[] infoGroup = collisionLoaders[i].GetCacheables(obj.Hash["HashId"].UInt);
                if (infoGroup == null) {
                    continue;
                }

                foreach (var info in infoGroup) {
                    collisionLoader.AddShape(info, 0, Vector3.Zero, Quaternion.Identity, Vector3.One);
                }

                using FileStream fs = File.Create(
                    Path.Combine(cacheDir, $"{obj.Hash["UnitConfigName"].String}.hksc"));
                collisionLoader.Save(fs);
            }

            _cached.Add(actorName);
        }
    }
}
ArchLeaders commented 1 year ago

Hopefully this has nothing to do with it xD

image