Git Product home page Git Product logo

Comments (11)

mikech2000 avatar mikech2000 commented on May 9, 2024 1

Works perfectly.
Thanks for a quick fix and detailed response.

from deform.

mikech2000 avatar mikech2000 commented on May 9, 2024 1

I have installed the development branch and find that the first frame problem has been fixed.
Git question: Is there a way to download the dev branch using a modified package manager URL (the one placed in manifest.json)? After trying to find an answer online, I finally applied the changes manually to the master branch in the package cache folder. Thanks.

from deform.

nathanvogel avatar nathanvogel commented on May 9, 2024 1

@mikech2000 You can change your manifest.json like this (~from Unity version 2018.3 I think)

{
  "dependencies": {
    "com.beans.deform": "https://github.com/keenanwoodall/Deform.git#5149d7907e36913c534959f7aff230511e9b23d5",
...

from deform.

nathanvogel avatar nathanvogel commented on May 9, 2024 1

Hi again @keenanwoodall ! Sorry for the late reply, other parts of the projects were more pressing ^^

Oh wow, I wasn't hoping for such a detailed answer! Thanks a lot! It's awesome to have such great support :) I'm trying to get my company to send a donation ;)

from deform.

keenanwoodall avatar keenanwoodall commented on May 9, 2024

tldr: Deformables have a single-frame delay to increase performance. Paste the code at the bottom over the DeformableManager.cs file for Deformables to update in a single frame

[edit] or you can set your deformable's manager reference to null

someDeformable.Manager = null;

which will unregister it and allow you to update it yourself using the following methods:

// Schedules the deformables jobs and returns a handle to them
someDeformable.Schedule();
// Forces the scheduled jobs to complete
someDeformable.Complete();
// Applies the modified mesh data. Make sure work is completed before calling this!
someDeformable.Apply();

The order of events in a single frame for Deform are as follows:

  1. Complete any scheduled work from the previous frame
  2. Apply changes to the mesh
  3. Schedule new work

To try and get better performance, Deform's calculations carry over to the next frame so there's always a one frame delay. I thought I was forcing the Deformable to update in a single frame when it gets enabled to prevent the single undeformed frame from being rendered, but evidently it isn't working.

I looked in the OnEnable method of the Deformable component and for some reason it's only forcing the first-frame update in edit mode 🤔

#if UNITY_EDITOR
if (!Application.isPlaying && handle.IsCompleted)
{
	Schedule ().Complete ();
	ApplyData ();
}
#endif

I removed the if statement and preprocessor directive so it always schedules, completes and applies changes on the first frame, but it still didn't work! It seemed the DeformableManager, (the singleton that manages the scheduling of deformables) was overriding the changes. I set the deformables reference to the manager to null (which unregisters it) and it updated correctly.

I'll see if I can sort this out, but in the meantime, if you don't mind sacrificing a bit of performance for single-frame updates, replace your DeformableManager.cs file with the following code which changes the order of events to:

  1. Schedule new work.
  2. Complete work.
  3. Apply changes.
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;

namespace Deform
{
	/// <summary>
	/// Manages scheduling deformables.
	/// </summary>
    [HelpURL ("https://github.com/keenanwoodall/Deform/wiki/DeformableManager")]
	public class DeformableManager : MonoBehaviour
	{
		private static readonly string DEF_MANAGER_NAME = "DefaultDeformableManager";

		private static DeformableManager defaultInstance;
		/// <summary>
		/// Returns the default manager.
		/// </summary>
		/// <param name="createIfMissing">If true, a manager will be created if one doesn't exist.</param>
		/// <returns></returns>
		public static DeformableManager GetDefaultManager (bool createIfMissing)
		{
			if (defaultInstance == null)
				defaultInstance = new GameObject (DEF_MANAGER_NAME).AddComponent<DeformableManager> ();
			return defaultInstance;
		}

		/// <summary>
		/// Should the manager update?
		/// </summary>
		public bool update = true;

		private HashSet<IDeformable> deformables = new HashSet<IDeformable> ();

		private void Start ()
		{
			if (update)
			{
				ScheduleDeformables ();
				CompleteDeformables ();
			}
		}

		private void Update ()
		{
			if (update)
			{
				ScheduleDeformables ();
				CompleteDeformables ();
			}
		}

		private void OnDisable ()
		{
			CompleteDeformables ();	
		}

		/// <summary>
		/// Creates a chain of work from the deformables and schedules it.
		/// </summary>
		public void ScheduleDeformables ()
		{
			foreach (var deformable in deformables)
				deformable.PreSchedule ();
			foreach (var deformable in deformables)
			{
				deformable.Schedule ();
			}

			// Schedule the chain.
			JobHandle.ScheduleBatchedJobs ();
		}

		/// <summary>
		/// Finishes the schedules work chain.
		/// </summary>
		public void CompleteDeformables ()
		{
			foreach (var deformable in deformables)
			{
				deformable.Complete();
				deformable.ApplyData();
			}
		}

		/// <summary>
		/// Registers a deformable to be updated by this manager.
		/// </summary>
		public void AddDeformable (IDeformable deformable)
		{
			deformables.Add (deformable);
		}

		/// <summary>
		/// Unregisters a deformable from this manager.
		/// </summary>
		public void RemoveDeformable (IDeformable deformable)
		{
			deformables.Remove (deformable);
		}
	}
}

from deform.

mikech2000 avatar mikech2000 commented on May 9, 2024

Need to reopen.

from deform.

keenanwoodall avatar keenanwoodall commented on May 9, 2024

@mikech2000 I looked into it further and you are right, the problem is still there. To test I was calling Debug.Break on the frame the deformable was instantiated. For some reason the deformable is correctly applied when the editor is paused via Debug.Break, but as soon as I removed the line I was able to notice the flicker. I'll try to find a functional solution as soon as possible, thanks for your patience.

from deform.

mikech2000 avatar mikech2000 commented on May 9, 2024

from deform.

keenanwoodall avatar keenanwoodall commented on May 9, 2024

@mikech2000 So the issue was a little further reaching than I thought. It was a mix of dealing with Unity's script execution order hell and a small bug. The change is currently on the development branch at this commit. Let me know if it works for you and it'll make its way into the next release.

from deform.

nathanvogel avatar nathanvogel commented on May 9, 2024

I'm having a similar issue, but with a setup of continuously rolling and deformed tiles, when one of the tiles is teleported (so not only on the first frame of the game). Is it possible to force to Deform to finish updating on the same frame? Would overriding DeformableManager as you described be the easiest way to do that?
And while I'm here, do you by any chance have a ~numbered performance cost of doing it on the same frame?

Here's a setup demonstrating the issue: (see the blue flash on the ground)
2019-11-27-16-31-04
The source:
DeformFrameDelayTest.zip

from deform.

keenanwoodall avatar keenanwoodall commented on May 9, 2024

@nathanvogel The performance gain of scheduling jobs on one frame and completing them on the next depends on a lot of factors. The basic thing to understand is that the jobs (the code that calculates the deformers) get scheduled but must also get completed. If the jobs get completed before the calculations are done, the main thread has to wait for them to complete which causes a lower framerate.

The best-case scenario is when the jobs finish their work before they get forced to complete. When this happens, the frame rate of the main thread is mostly unaffected by all the calculations that were done on the worker threads.

The issue with doing single frame updates is that the jobs are scheduled in Update and either completed immediately or in LateUpdate. If the jobs are completed in LateUpdate, they only get to run for the duration of the game logic before they start affecting the performance of the main thread. If you don't have a lot of game logic, the main thread will probably have to wait for the jobs to complete.

In the images below, the purple line shows the parts of the frame the jobs are able to run.

Single Frame Update

SingleFrameUpdate

In pursuit of giving the jobs as much time to run as possible without affecting the main thread, Deform schedules work and doesn't complete it until the next frame. This introduces a single frame of latency, but also introduces the opportunity for much better performance.

Double Frame Update

DoubleFrameUpdate

As you can see, the "frame buffer" allows the jobs to run during the physics, input, rendering and decommissioning portions of the frame in addition to the game logic. So it doesn't really take two frames worth of time to update, it just starts the jobs halfway through frame n and doesn't complete until halfway through frame n+1.

Using the unity execution order graph in conjunction with knowledge of which parts of your game take up what portion of a frame you can judge the performance penalty you'll be paying for single frame updates.

The best thing you can do is to profile your application (in a build). See how long it takes for DeformableManager.CompleteDeformables() to run. You can profile it with a StopWatch, or enable deep profiling in the Unity profiler. If the method is taking an extremely short amount of time, you can assume the jobs aren't holding up the main thread (or a very small amount of work is being scheduled)

Here's the profiler in a simple example scene I made (using the default "frame buffered manager"). You can see CompleteDeformables() takes 0.009ms.

profiling1

For the next test I tweaked the DeformableManager.cs script from the development branch to schedule in Update and complete in LateUpdate (it updates in a single frame.) In the same test scene CompleteDeformables() took 0.64ms which indicates the main thread had to wait on the jobs to complete.

profiling2

So even in a simple test scene the different was more than half a millisecond. However, that's not an entirely fair test as the only thing running in the scene was a deformable. If I had other game code running, it would have given more time for the jobs to complete and CompleteDeformables() wouldn't have taken as long to complete since it wouldn't have as much work to wait on.

I recommend trying single frame updates and seeing if the impact is significant. Here's the modified version of the DeformableManager I used in the test for single frame updates. It has had minimal testing, but worked for me.

tldr: The performance impact depends on a lot of things. Profile it to see if the impact actually matters in your game. Don't feel the need to use the deformable manager for everything. You're free to choose certain deformables you want to update manually.

Here's an example script I wrote that should handle unregistering an assigned deformable from the manager and updating it manually in a single frame. It's quite easy to understand, so if necessary you should be able to repurpose it or extrapolate it into a custom manager that suits your needs directly.

using UnityEngine;
using Deform;

public class DeformableUpdater : MonoBehaviour
{
	public Deformable deformable;

	private void OnEnable ()
	{
		if (deformable == null)
			return;
		deformable.Manager = null; // If you use the newest commit on the develop branch, setting UpdateMode to Custom automatically sets Manager to null
		deformable.UpdateMode = UpdateMode.Custom;
	}
	private void OnDisable()
	{
		if (deformable == null)
			return;
		deformable.Complete();
	}
	private void Update ()
	{
		if (deformable == null)
			return;
		deformable.PreSchedule();
		deformable.Schedule ();
	}
	private void LateUpdate ()
	{
		if (deformable == null)
			return;
		deformable.Complete ();
		deformable.ApplyData ();
	}
}

from deform.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.