maximizing performance of 3 d user generated assets in unity

27
Maximizing performance of 3D user-generated assets in Unity Max Pixel Freeform Labs

Upload: withthebest

Post on 17-Jan-2017

638 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Maximizing performance of 3 d user generated assets in unity

Maximizing performance of 3D user-generated

assets in UnityMax Pixel

Freeform Labs

Page 2: Maximizing performance of 3 d user generated assets in unity

DBA since May 2015Incorporated March 2016

Your Speaker

2003Games & Websites as a hobby

2008Freelance

web development

2011 - 2014 - 2015USC Film Production

→Interactive Entertainment

Freeform Labs

Inception Feb 2014Funded March 2016

Page 5: Maximizing performance of 3 d user generated assets in unity

Q: Why is it freezing?

Page 6: Maximizing performance of 3 d user generated assets in unity

A: GARBAGECOLLECTION

Page 7: Maximizing performance of 3 d user generated assets in unity

The AgendaOptimization principles

Real examples

???

PROFIT

Page 8: Maximizing performance of 3 d user generated assets in unity

Hands-on Demonstration!

Starting Point:http://wiki.unity3d.com/index.php/OptimizedTrailRenderer

https://github.com/M-Pixel/unity-procedural-mesh-optimization-presentation

Page 9: Maximizing performance of 3 d user generated assets in unity

Encapsulation grumble grumblepublic float maxAngle → [SerializeField] private float _maxAngle

Hold down alt for magic Buy ReSharper for more magic

Full disclosure: I have no affiliation with ReSharper. They don’t give me money. I just really like it.

Page 10: Maximizing performance of 3 d user generated assets in unity

Startprivate void Start(){ _trailObj = new GameObject("Trail"); // Why have a game object that just controls another one? _trailObj.transform.parent = null; _trailObj.transform.position = Vector3.zero; _trailObj.transform.rotation = Quaternion.identity; _trailObj.transform.localScale = Vector3.one; var meshFilter = (MeshFilter)_trailObj.AddComponent(typeof(MeshFilter)); _mesh = meshFilter.mesh; _trailObj.AddComponent(typeof(MeshRenderer)); _instanceMaterial = new Material(_material); _fadeOutRatio = 1f / _instanceMaterial.GetColor("_TintColor").a; _trailObj.GetComponent<Renderer>().material = _instanceMaterial;}

Prefer Observation[SerializeField] private Transform _source;

RequireComponent[RequireComponent(typeof(MeshFilter))][RequireComponent(typeof(MeshRenderer))]public class Trail : MonoBehaviour {

Destroy(_trailObj);Destroy(this); // ???

New Startprivate void Start(){ _mesh = GetComponent<MeshFilter>().mesh; _instanceMaterial = new Material(_material); _fadeOutRatio = 1f / _instanceMaterial.GetColor("_TintColor").a; _renderer = GetComponent<MeshRenderer>(); _renderer.material = _instanceMaterial;}

(because the mesh origin needs to be stationary, that’s why)

Page 11: Maximizing performance of 3 d user generated assets in unity

Avoid Reflectionif (_pointCnt < 2){

_trailObj.GetComponent<Renderer>().enabled = false;return;}_trailObj.GetComponent<Renderer>().enabled = true;

Revisedif (_pointCnt < 2){ _renderer.enabled = false; return;}_renderer.enabled = true;

Add to Classprivate MeshRenderer _renderer;

In Start_renderer = GetComponent<MeshRenderer>();

Page 12: Maximizing performance of 3 d user generated assets in unity

Optimize by actually optimizing, not reducing quality

// Optimizationif (_pointCnt > _optimizeCount){ _maxAngle += _optimizeAngleInterval; _maxVertexDistance += _optimizeDistanceInterval; _optimizeCount += 1;}

Page 13: Maximizing performance of 3 d user generated assets in unity

Heap vs StackLong-term, Garbage Collected● Class instances● Arrays

○ Even tiny ones ( ノ ಥ益ಥ) ノ

Very short-term● POD● Structs

○ … sometimes

Page 14: Maximizing performance of 3 d user generated assets in unity

Points are being deleted and reallocated at runtimefor (var i = _pointCnt - 1; i >= 0; i--) {

var point = _points[i];if (point == null || point.TimeAlive > _segmentLifetime) {

_points[i] = null;_pointCnt--;

} else break;}if (_emit) {

if (_pointCnt == 0) {_points[_pointCnt++] = new Point(_source.transform);_points[_pointCnt++] = new Point(_source.transform);}...

Page 15: Maximizing performance of 3 d user generated assets in unity

indices > _pointCnt are ignored anywaysfor (var i = _pointCnt - 1; i >= 0; i--) {

var point = _points[i];if (point == null || point.TimeAlive > _segmentLifetime) {

_points[i] = null;_pointCnt--;

} else break;}if (_emit) {

// Make sure there are always at least 2 points when emittingif (_pointCnt < 2) {if (_pointCnt < 1) InsertPoint();InsertPoint();}...

Page 16: Maximizing performance of 3 d user generated assets in unity

RAM & Speed

Putting things away and taking them out again takes a lot of time

Page 17: Maximizing performance of 3 d user generated assets in unity

Shifting is unnecessarily expensiveRemoving old points

for (var i = _pointCnt - 1; i >= 0; i--){var point = _points[i];if (point.TimeAlive >

_segmentLifetime) {_pointCnt--;

} else break;}

Adding new points

for(int i = pointCnt; i > 0; i--)points[i] = points[i-1];

points[0] = new Point(transform);pointCnt++;

What about adding new points on top of the array?

Just reverses problem

What about a Queue?

Yes!

Page 18: Maximizing performance of 3 d user generated assets in unity

using (var e = _ints.GetEnumerator()) {while (e.MoveNext()) {

_current += e.Current;}}

for (var i = 0; i < _ints.Count; i++) {var item = _ints.Dequeue();_current += item;_ints.Enqueue(item);}

Unity’s Enumerator Problem

Page 19: Maximizing performance of 3 d user generated assets in unity

Temporary Arrays

// Rebuild itvar vertices = new Vector3[pointCount * 2];var uvs = new Vector2[pointCount * 2];var triangles = new int[(pointCount - 1) * 6];var meshColors = new Color[pointCount * 2];

5.3 MB/s

Page 20: Maximizing performance of 3 d user generated assets in unity

Comparing Distance

var sqrDistance = (_points[1].Position -_source.transform.position

).sqrMagnitude;

if (sqrDistance > _minVertexDistance * _minVertexDistance){

…}

if (Vector3.Dot(a, b) > _preSquaredBasis) …

Page 21: Maximizing performance of 3 d user generated assets in unity

Comparing Distance

a2 + b2 = c2

a2 + b2 + c2 = d2

d = sqrt ( a2 + b2 + c2 )

Square root is a relatively expensive operation

Dot == a2 + b2 + c2

Vector3.Distance == Sqrt ( Dot ( A , B ) )

Comparing dots is faster than comparing distances

Double trouble: Unity’s Vector3.Dot uses doublesSo if you wanna go real fast, make your own Dot

Page 22: Maximizing performance of 3 d user generated assets in unity

Other Optimizations

Use arrays for vertex, color, uv buffers (triangle buffer must be a List)

Make Point a struct (Pre-allocate)

Eliminate (inline) pool

Eliminate Points altogether, work directly with vertices?

Page 23: Maximizing performance of 3 d user generated assets in unity

What about extra vertices?

It turns out, any number of extra verts/colors/etc. have negligible impact

Only triangles matter!

mesh.SetTriangles takes a List

Page 24: Maximizing performance of 3 d user generated assets in unity

The final result...

Page 25: Maximizing performance of 3 d user generated assets in unity

34 to 60KB garbage + .41msvs

0B garbage + .24ms(per frame)

Page 26: Maximizing performance of 3 d user generated assets in unity

Takeaways

Use the Unity Profiler’s gcalloc column to find out where heap allocation occurs

First use utilities like Queue, List to eliminate GC, then once your pattern is confirmed to work and the design is locked down, you can refactor to naked arrays

Reuse Arrays, Delegates, G.O.s whenever possible

Start/end indexes

Pool unused instances

Don’t use IEnumerators, find a workaround

When comparing distances, use Dot instead of Distance

Mesh data: Use List for triangles, Array for everything else

Multithreading is easy! Use queue + lock, and good encapsulation

Page 27: Maximizing performance of 3 d user generated assets in unity

Thank [email protected]