Browse Source

add scene profiler

trethaller 2 years ago
parent
commit
ab5adbd4f9
4 changed files with 232 additions and 2 deletions
  1. 220 0
      h3d/impl/SceneProf.hx
  2. 3 1
      h3d/pass/Default.hx
  3. 3 1
      h3d/scene/Object.hx
  4. 6 0
      h3d/scene/Scene.hx

+ 220 - 0
h3d/impl/SceneProf.hx

@@ -0,0 +1,220 @@
+#if sceneprof
+package h3d.impl;
+
+private class Frame {
+	public var samples : Array<{ time : Float, sect: String, stack : Array<String> }> = [];
+	public var startTime : Float;
+	public function new() {
+	}
+}
+
+private class StackLink {
+	static var UID = 1;
+	public var id : Int;
+	public var elt : String;
+	public var parent : StackLink;
+	public var children : Map<String,StackLink> = new Map();
+	public var written : Bool;
+	public function new(elt) {
+		id = UID++;
+		this.elt = elt;
+	}
+	public function getChildren(elt:String) {
+		var c = children.get(elt);
+		if( c == null ) {
+			c = new StackLink(elt);
+			c.parent = this;
+			children.set(elt,c);
+		}
+		return c;
+	}
+}
+
+class SceneProf {
+	static var frames : Array<Frame>;
+	static var curFrame : Frame;
+	static var lastFrameId = -1;
+	static var enable = false;
+    static var curSection : String;
+
+	static var stackCache : Map<h3d.scene.Object, Array<String>>;
+
+	public static function start() {
+		enable = true;
+		stackCache = new Map();
+		frames = [];
+		lastFrameId = -1;
+	}
+
+	public static function stop() {
+		enable = false;
+	}
+
+	public static function begin(section: String, ctx : h3d.scene.RenderContext) {
+		if(!enable) return;
+		if(ctx.frame != lastFrameId) {
+			var f = new Frame();
+			f.startTime = Sys.time();
+			frames.push(f);
+			curFrame = f;
+			lastFrameId = ctx.frame;
+		}
+        curSection = section;
+	}
+
+	static function getStack(o: h3d.scene.Object) : Array<String> {
+		var r = stackCache.get(o);
+		if(r != null)
+			return r;
+		var s = null;
+		if(o.parent != null)
+			s = getStack(o.parent).copy();
+		else s = [];
+
+		var name = o.name != null ? o.name : Type.getClassName(Type.getClass(o));
+		s.unshift(name);
+		stackCache.set(o, s);
+		return s;
+	}
+
+	public static function mark(obj: h3d.scene.Object) {
+		if(!enable) return;
+		var t = Sys.time();
+		curFrame.samples.push({
+			time: t,
+			sect: curSection,
+			stack: getStack(obj)
+		});
+	}
+
+	public static function end() {
+		if(!enable) return;
+		curFrame.samples.push({
+			time: Sys.time(),
+			sect: curSection,
+			stack: []
+		});
+	}
+
+	public static function save(outFile: String) {
+		function makeStacks( st : Array<StackLink> ) {
+			var write = [];
+			for( s in st ) {
+				var s = s;
+				while( s != null ) {
+					if( s.written ) break;
+					s.written = true;
+					write.push(s);
+					s = s.parent;
+				}
+			}
+			write.sort(function(s1,s2) return s1.id - s2.id);
+			return [for( s in write ) {
+				callFrame : {
+					functionName : s.elt,
+					scriptId : 0,
+				},
+				id : s.id,
+				parent : s.parent == null ? null : s.parent.id,
+			}];
+		}
+
+		var json : Array<Dynamic> = [
+			{
+    			pid : 0,
+    			tid : 0,
+ 	 			ts : 0,
+				ph : "M",
+				cat : "__metadata",
+				name : "thread_name",
+				args : { name : "CrBrowserMain" }
+			}
+		];
+
+		var count = 1;
+		var f0 = frames[0];
+		var t0 = f0.samples.length == 0 ? f0.startTime : f0.samples[0].time;
+
+		var tid = 0;
+
+		function timeStamp(t:Float) {
+			return Std.int((t - t0) * 1000000) + 1;
+		}
+
+		var lastT = 0.;
+		var rootStack = new StackLink("(root)");
+		var profileId = count++;
+
+		json.push({
+			pid : 0,
+			tid : tid,
+			ts : 0,
+			ph : "P",
+			cat : "disabled-by-default-v8.cpu_profiler",
+			name : "Profile",
+			id : "0x"+profileId,
+			args: { data : { startTime : 0 } },
+		});
+
+		for( f in frames ) {
+			if( f.samples.length == 0 ) continue;
+			json.push({
+				pid : 0,
+				tid : tid,
+				ts : timeStamp(f.startTime),
+				ph : "B",
+				cat : "devtools.timeline",
+				name : "FunctionCall",
+			});
+			json.push({
+				pid : 0,
+				tid : tid,
+				ts : timeStamp(f.samples[f.samples.length-1].time),
+				ph : "E",
+				cat : "devtools.timeline",
+				name : "FunctionCall"
+			});
+		}
+		for( f in frames ) {
+			if( f.samples.length == 0 ) continue;
+
+			var timeDeltas = [];
+			var allStacks = [];
+			var lines = [];
+
+			for( s in f.samples) {
+				var st = rootStack;
+				st = st.getChildren(s.sect);
+				var line = 0;
+				for( i in 0...s.stack.length ) {
+					var s = s.stack[s.stack.length - 1 - i];
+					st = st.getChildren(s);
+				}
+				allStacks.push(st);
+				var t = Math.ffloor((s.time - t0) * 1000000);
+				timeDeltas.push(t - lastT);
+				lastT = t;
+			}
+			json.push({
+				pid : 0,
+				tid : tid,
+				ts : 0,
+				ph : "P",
+				cat : "disabled-by-default-v8.cpu_profiler",
+				name : "ProfileChunk",
+				id : "0x"+profileId,
+				args : {
+					data : {
+						cpuProfile : {
+							nodes : makeStacks(allStacks),
+							samples : [for( s in allStacks ) s.id]
+						},
+						timeDeltas : timeDeltas,
+					}
+				}
+			});
+		}
+		sys.io.File.saveContent(outFile, haxe.Json.stringify(json));
+	}
+}
+#end

+ 3 - 1
h3d/pass/Default.hx

@@ -94,6 +94,7 @@ class Default extends Base {
 	override function draw( passes : h3d.pass.PassList, ?sort : h3d.pass.PassList -> Void ) {
 		if( passes.isEmpty() )
 			return;
+		#if sceneprof h3d.impl.SceneProf.begin("draw", ctx); #end
 		for( g in ctx.sharedGlobals )
 			globals.fastSet(g.gid, g.value);
 		setGlobals();
@@ -105,6 +106,7 @@ class Default extends Base {
 		ctx.currentManager = manager;
 		var buf = ctx.shaderBuffers, prevShader = null;
 		for( p in passes ) {
+			#if sceneprof h3d.impl.SceneProf.mark(p.obj); #end
 			globalModelView = p.obj.absPos;
 			if( p.shader.hasGlobal(globalModelViewInverse_id.toInt()) )
 				globalModelViewInverse = p.obj.getInvPos();
@@ -126,7 +128,7 @@ class Default extends Base {
 			}
 			drawObject(p);
 		}
+		#if sceneprof h3d.impl.SceneProf.end(); #end
 		ctx.nextPass();
 	}
-
 }

+ 3 - 1
h3d/scene/Object.hx

@@ -706,6 +706,7 @@ class Object {
 	}
 
 	function syncRec( ctx : RenderContext ) {
+		#if sceneprof h3d.impl.SceneProf.mark(this); #end
 		if( currentAnimation != null ) {
 			var old = parent;
 			var dt = ctx.elapsedTime;
@@ -781,9 +782,10 @@ class Object {
 	}
 
 	function emitRec( ctx : RenderContext ) {
+		#if sceneprof h3d.impl.SceneProf.mark(this); #end
 
 		if( !visible || (culled && inheritCulled && !ctx.computingStatic) )
-			return;
+			return;		
 
 		// fallback in case the object was added during a sync() event and we somehow didn't update it
 		if( posChanged ) {

+ 6 - 0
h3d/scene/Scene.hx

@@ -403,8 +403,14 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 		ctx.start();
 		renderer.start();
 
+		#if sceneprof h3d.impl.SceneProf.begin("sync", ctx); #end
 		syncRec(ctx);
+		#if sceneprof
+		h3d.impl.SceneProf.end();
+		h3d.impl.SceneProf.begin("emit", ctx);
+		#end
 		emitRec(ctx);
+		#if sceneprof h3d.impl.SceneProf.end(); #end
 
 		var passes = [];
 		var passIndex = -1;