Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize mesh serialization #10

Closed
keenanwoodall opened this issue Mar 30, 2019 · 22 comments
Closed

Optimize mesh serialization #10

keenanwoodall opened this issue Mar 30, 2019 · 22 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@keenanwoodall
Copy link
Owner

keenanwoodall commented Mar 30, 2019

Every deformable has its dynamic mesh serialized as a unique mesh. This bloats scene file size. It's unnecessary because the dynamic mesh can be generated from the original mesh. MeshData.DynamicMesh isn't serialized, but the target (Mesh Filter or Skinned Mesh Renderer) uses it and is serialized. Somehow the targets mesh reference needs to be overridden when serialized, to point to the original mesh. Then at runtime the dynamic mesh can be generated.

@keenanwoodall keenanwoodall added enhancement New feature or request help wanted Extra attention is needed labels Mar 30, 2019
@keenanwoodall keenanwoodall changed the title Optimize Mesh serialization Optimize mesh serialization Mar 30, 2019
@Joen-UnLogick
Copy link

Joen-UnLogick commented Apr 13, 2019

How about this idea?
In UnityEditor outside of playmode don't set the MeshFilter mesh as you normally do, instead do:

  • OnPreCull: set the mesh filter to the dynamic mesh
  • OnPostRender: restore the original mesh

Edit: Changed the set to OnPreCull...

@keenanwoodall
Copy link
Owner Author

@Joen-UnLogick That's a good thought, I'll look into it. Unfortunately, one problem I've created for myself is that the Deformable checks if the mesh filter's mesh matches the mesh being deformed. If it doesn't, the deformable reinitializes to deform the new mesh. This is meant to make it easy to change the deformed mesh, but will make things messy if I want to change to a non-unique mesh. I honestly need to rewrite the Deformable component eventually. It's a part of Deform that stands out to me as being poorly structured.

@Joen-UnLogick
Copy link

I'd love to help, unfortunately I'm completely swamped. I've only downloaded Deform so far, hopefully within the next month I'll find time to actually try it out. :D

Major props to you for having the foresight to populate "help wanted" issues like this. It allows me to pitch in on what looks like an awesome project without actually doing anything.

@bleppie
Copy link

bleppie commented May 17, 2019

First off, impressive package! I prefer the way you've structured it to MegaFiers'. Yours is more intuitive.

I've been trying Deform on a mesh with 16000+ vertices and am finding having a Deformable increases my save times from less than 1 second to 30 seconds or more. I chased this down to the Deformable replacing the Skinned-Mesh-Renderer's mesh with a clone, which is then serialized by Unity. So I think this is the same problem you mentioned above.

I used a variation of @Joen-UnLogick's solution, details below. OnPreCull/OnPostRender only work on Cameras, so I used OnWillRenderObject/OnRenderObject. From your comments it will break if I change the incoming mesh, but that's not an issue for me and for now it brings save times down to less than 1 second again. So a hack, but workable.

 #if UNITY_EDITOR
      public void OnWillRenderObject() {
          if (! UnityEditor.EditorApplication.isPlaying) {
              data.Target.SetMesh(data.DynamicMesh);
          }
      }
      public void OnRenderObject() {
          if (! UnityEditor.EditorApplication.isPlaying) {
              data.Target.SetMesh(data.OriginalMesh);
          }
      }
#endif

Edit: I forgot, I also had to mark the DynamicMesh as [NonSerialized] in MeshData:

    [NonSerialized]
    public Mesh DynamicMesh;

@keenanwoodall
Copy link
Owner Author

@bleppie I'm glad you were able to find a solution that works for you. Thanks for sharing it! I'm gonna see if I can find a way to implement this serialization hack into the main branch, but I'll need to see if I can make it work without breaking anything else. In an ideal world there'd be an OnSerialize callback which would make things much easier!

@carter1990
Copy link

carter1990 commented May 18, 2019 via email

@carter1990
Copy link

carter1990 commented May 18, 2019 via email

@keenanwoodall
Copy link
Owner Author

@carter1990 will do, thanks!

@bleppie
Copy link

bleppie commented May 20, 2019

Just an update from my end. The hack/solution I proposed above requires one more bit of code in Deformable to reset the mesh after playing.

private void OnPlayModeState(UnityEditor.PlayModeStateChange state) {
         if (state == UnityEditor.PlayModeStateChange.ExitingPlayMode) {
             data.Target.SetMesh(data.OriginalMesh);
         }
     }

I tried ISerializationCallBackReceiver as suggested by @carter1990, but found that OnBeginSerialize was being called many, many times without a corresponding OnAfterSerialize, so I couldn't reset the mesh correctly. There's some more info here on the call, but it doesn't explain why OnAfterSerialize isn't bring called.

@carter1990
Copy link

carter1990 commented May 21, 2019 via email

@HitCache
Copy link

HitCache commented Aug 5, 2020

Before I dive deeper trying to find a solution, @keenanwoodall checking in if you or anyone else here has hopefully had any progress/ideas on this?

The serialization has bloated my scene sizes over 50,000% (from 29kb to 14.6mb on one scene for example, I use it across multiple scenes and the bloating is compounding hard) so I'm keen to figure something out asap, even if it's a workaround.

I tried @bleppie 's code and it helped somewhat but obviously not all the way.

As a workaround I'm thinking about maybe disabling the deformable component during scene build (via OnProcessScene) and then enabling the deformable in play mode so that it grabs fresh references from the existing mesh filter, but I can't figure out what might be the most proper/safest way to nuke the existing serialized data during the scene bake stage.

If you could point me in the right direction please I could at least get a workaround going in the meantime and would greatly appreciate it!!

@bleppie
Copy link

bleppie commented Aug 5, 2020

Hi, I unfortunately don't have anything new---I haven't worked on this since my comment---but please post what you find out.

@keenanwoodall
Copy link
Owner Author

I'm not sure if this is a new API, but I looked for callbacks on scene saving and found this delegate

I plugged into it from the Deformable to set the mesh filters mesh to the original mesh on save and it seems to work! I've only just tested this so make sure you backup your project before trying it out. There could be something wrong with my code that I'm not noticing, but it seems to be working great as of now!

Here's the changes, but I'm also gonna push to the develop branch if you wanna pull from there.

Deformable.OnEnable

private void OnEnable()
{
     InitializeData();

     if (Application.isPlaying)
          Manager = DeformableManager.GetDefaultManager(true);

     #if UNITY_EDITOR
     if (!Application.isPlaying && handle.IsCompleted)
     {
          PreSchedule();
          Schedule().Complete();
          ApplyData();
     }
			
     // Add this line
     UnityEditor.SceneManagement.EditorSceneManager.sceneSaving += OnSceneSaving;
     #endif
}

Deformable.OnDisable

protected virtual void OnDisable()
{
     Complete();
     data.Dispose(assignOriginalMeshOnDisable);
     if (Manager != null)
          Manager.RemoveDeformable(this);
			
     // Add the next three lines
     #if UNITY_EDITOR
     UnityEditor.SceneManagement.EditorSceneManager.sceneSaving -= OnSceneSaving;
     #endif
}

Deformable.OnSceneSaving

// Add this function somewhere on the Deformable
#if UNITY_EDITOR
private void OnSceneSaving(Scene scene, string path)
{
     if (data != null && data.Target != null)
          data.Target.SetMesh(data.OriginalMesh);
}
#endif

@keenanwoodall
Copy link
Owner Author

keenanwoodall commented Aug 5, 2020

After thinking about it for a bit I'm betting there's gonna be some issues with prefabs since they don't exist in the scene but will still probably get scene saving callbacks. That's where getting a dependable serialization callback would be helpful, but I bet I can detect if a deformable is a prefab in the Projects folder vs a deformable in the scene and act accordingly. I'll have to look into this later.

@HitCache
Copy link

HitCache commented Aug 5, 2020

Trying this now.
I can't thank you enough, seriously.

@HitCache
Copy link

HitCache commented Aug 5, 2020

I think there might be an edge case where already serialized data on a currently disabled deformable won't get swept with the OnSceneSaving function until you enable first then save the scene then disable and save again, but that aside it seems to work perfectly. Thank you again for helping with this so quickly!

@HitCache
Copy link

HitCache commented Aug 5, 2020

For what it's worth in case anyone else needs it, in MeshData.cs I also have this as @bleppie suggested:

[NonSerialized]
public Mesh DynamicMesh;

and I did this based on your last commit in the develop branch:

removed -> [SerializeField, HideInInspector]

from

private ManagedMeshData originalManaged;

@keenanwoodall
Copy link
Owner Author

keenanwoodall commented Aug 5, 2020

oh yup I forgot to mention that part! thanks for catching that!

I think there might be an edge case where already serialized data on a currently disabled deformable won't get swept with the OnSceneSaving function until you enable first then save the scene then disable and save again

interesting, i'll look into it!

@HitCache
Copy link

HitCache commented Aug 5, 2020

It's simply because OnSceneSaving handler hasn't been attached yet since OnEnable hasn't been called.

@keenanwoodall
Copy link
Owner Author

keenanwoodall commented Aug 8, 2020

I've done a bit more testing and prefabs don't seem to be an issue! 🤷‍♀️

I made a prefab with a bunch of default deformable cubes
GipvCPfH6A
It's only 519 KB
image

I put a few of the prefabs into a scene like so
QRxMZImv4u

It was super slow, but the scene was only 213 KB!
image

There's definitely still a hiccup when loading a deformable-heavy scene since all the deformables have to initialize, but I'm pretty sure it's faster than loading all the meshes off disk, so it's a win in all regards.

If anyone runs into any issues or bugs with the changes let me know. I'd like to merge to main, but wanna be a little cautious so I might wait a bit.

@HitCache
Copy link

HitCache commented Aug 8, 2020

For what it's worth I have been using the latest changes discussed here in production and it's been flawless so far!

@keenanwoodall
Copy link
Owner Author

Awesome!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

5 participants