123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- package h3d.impl;
- private class QueryObject {
- var driver : h3d.impl.Driver;
- public var q : h3d.impl.Driver.Query;
- public var value : Float;
- public var name : String;
- public var drawCalls : Int;
- public var next : QueryObject;
- public function new() {
- driver = h3d.Engine.getCurrent().driver;
- q = driver.allocQuery(TimeStamp);
- }
- public function sync() {
- value = driver.queryResult(q);
- }
- public function isAvailable() {
- return driver.queryResultAvailable(q);
- }
- public function dispose() {
- driver.deleteQuery(q);
- q = null;
- }
- }
- private class StatsObject {
- public var name : String;
- public var time : Float;
- public var drawCalls : Int;
- public var next : StatsObject;
- public var xPos : Int;
- public var xSize : Int;
- public function new() {
- }
- }
- class Benchmark extends h2d.Graphics {
- var cachedStats : StatsObject;
- var currentStats : StatsObject;
- var cachedQueries : QueryObject;
- var currentFrame : QueryObject;
- var waitFrames : Array<QueryObject>;
- var engine : h3d.Engine;
- var stats : StatsObject;
- var labels : Array<h2d.Text>;
- var interact : h2d.Interactive;
- public var estimateWait = false;
- public var enable(default,set) : Bool;
- public var width : Null<Int>;
- public var height = 16;
- public var textColor = 0;
- public var colors = new Array<Int>();
- public var font : h2d.Font;
- public var recalTime = 1e9;
- public var smoothTime = 0.95;
- public var measureCpu = false;
- var tip : h2d.Text;
- var tipCurrent : StatsObject;
- var tipCurName : String;
- var curWidth : Int;
- var prevFrame : Float;
- var frameTime : Float;
- public function new(?parent) {
- super(parent);
- waitFrames = [];
- labels = [];
- engine = h3d.Engine.getCurrent();
- interact = new h2d.Interactive(0,0,this);
- interact.onMove = onMove;
- interact.cursor = Default;
- interact.onOut = function(_) {
- if( tip == null ) return;
- tip.parent.remove();
- tip = null;
- tipCurrent = null;
- }
- enable = engine.driver.hasFeature(Queries);
- }
- function set_enable(e) {
- if( !e )
- cleanup();
- return enable = e;
- }
- function cleanup() {
- while( waitFrames.length > 0 ) {
- var w = waitFrames.pop();
- while( w != null ) {
- w.dispose();
- w = w.next;
- }
- }
- while( cachedQueries != null ) {
- cachedQueries.dispose();
- cachedQueries = cachedQueries.next;
- }
- while( currentFrame != null ) {
- currentFrame.dispose();
- currentFrame = currentFrame.next;
- }
- }
- override function clear() {
- super.clear();
- if( labels != null ) {
- for( t in labels ) t.remove();
- labels = [];
- }
- if( interact != null ) interact.width = interact.height = 0;
- }
- override function onRemove() {
- super.onRemove();
- cleanup();
- }
- function onMove(e:hxd.Event) {
- var s = currentStats;
- while( s != null ) {
- if( e.relX >= s.xPos && e.relX <= s.xPos + s.xSize )
- break;
- s = s.next;
- }
- if( tip == null ) {
- var fl = new h2d.Flow(this);
- fl.y = -23;
- fl.backgroundTile = h2d.Tile.fromColor(0,1,1,0.8);
- fl.padding = 5;
- tip = new h2d.Text(font, fl);
- tip.dropShadow = { dx : 0, dy : 1, color : 0, alpha : 1 };
- }
- tipCurrent = s;
- tipCurName = s == null ? null : s.name;
- syncTip(s);
- }
- function syncTip(s:StatsObject) {
- if( s == null )
- tip.text = "total "+engine.drawCalls+" draws "+hxd.Math.fmt(engine.drawTriangles/1000000)+" Mtri";
- else
- tip.text = s.name+"( " + Std.int(s.time / 1e6) + "." + StringTools.lpad(""+(Std.int(s.time/1e4)%100),"0",2) + " ms " + s.drawCalls + " draws )";
- var tw = tip.textWidth + 10;
- var tx = s == null ? curWidth : s.xPos + ((s.xSize - tw) * .5);
- if( tx + tw > curWidth ) tx = curWidth - tw;
- if( tx < 0 ) tx = 0;
- if( hxd.Math.abs(tip.parent.x - tx) > 5 ) tip.parent.x = Std.int(tx);
- }
- public function begin() {
- if( !enable ) return;
- var t0 = haxe.Timer.stamp();
- var ft = (t0 - prevFrame) * 1e9;
- if( hxd.Math.abs(ft - frameTime) > recalTime )
- frameTime = ft;
- else
- frameTime = frameTime * smoothTime + ft * (1 - smoothTime);
- prevFrame = t0;
- // end was not called...
- if( currentFrame != null )
- end();
- // sync with available frame
- var changed = false;
- while( waitFrames.length > 0 ) {
- var q = waitFrames[0];
- if( !q.isAvailable() )
- break;
- waitFrames.shift();
- // recycle previous stats
- var st = currentStats;
- while( st != null ) {
- var n = st.next;
- st.next = cachedStats;
- cachedStats = st;
- st = n;
- }
- currentStats = null;
- var prev : QueryObject = null;
- var totalTime = 0.;
- while( q != null ) {
- if( !measureCpu ) q.sync();
- if( prev != null ) {
- var dt = prev.value - q.value;
- var s = allocStat(q.name, dt);
- totalTime += dt;
- s.drawCalls = prev.drawCalls - q.drawCalls;
- if( s.drawCalls < 0 ) s.drawCalls = 0;
- }
- // recycle
- var n = q.next;
- q.next = cachedQueries;
- cachedQueries = q;
- prev = q;
- q = n;
- }
- if( estimateWait ) {
- var waitT = frameTime - totalTime;
- if( waitT > 0 ) {
- if( hxd.Window.getInstance().vsync ) {
- var vst = 1e9 / hxd.System.getDefaultFrameRate() - totalTime;
- if( vst > waitT ) vst = waitT;
- if( vst > 0 ) {
- var s = allocStat("vsync", vst);
- s.drawCalls = 0;
- waitT -= vst;
- }
- }
- if( waitT > 0.5e6 /* 0.5 ms */ ) {
- var s = allocStat(measureCpu ? "gpuwait" : "cpuwait", waitT);
- s.drawCalls = 0;
- }
- }
- }
- // stats updated
- changed = true;
- }
- if( allocated && visible )
- syncVisual();
- measure("begin");
- }
- function syncVisual() {
- var s2d = getScene();
- var old = labels;
- labels = null;
- clear();
- labels = old;
- var width = width == null ? s2d.width : width;
- curWidth = width;
- beginFill(0, 0.5);
- drawRect(0, 0, width, height);
- interact.width = width;
- interact.height = height;
- var totalTime = 0.;
- var s = currentStats;
- while( s != null ) {
- totalTime += s.time;
- s = s.next;
- }
- var space = 52;
- width -= space;
- var count = 0;
- var xPos = 0;
- var curTime = 0.;
- var s = currentStats;
- while( s != null ) {
- if( colors.length <= count ) {
- var color = new h3d.Vector();
- var m = new h3d.Matrix();
- m.identity();
- m.colorHue(count);
- color.setColor(0x3399FF);
- color.transform(m);
- colors.push(color.toColor());
- }
- curTime += s.time;
- var xEnd = Math.ceil(width * (curTime / totalTime));
- var xSize = xEnd - xPos;
- beginFill(colors[count]);
- drawRect(xPos, 0, xSize, height);
- var l = allocLabel(count);
- if( xSize < s.name.length * 6 )
- l.visible = false;
- else {
- l.visible = true;
- l.textColor = textColor;
- l.text = s.name;
- l.x = xPos + Std.int((xSize - l.textWidth) * .5);
- }
- s.xPos = xPos;
- s.xSize = xSize;
- if( tipCurrent == s && tipCurName == s.name )
- syncTip(s);
- xPos = xEnd;
- count++;
- s = s.next;
- }
- if( tip != null && tipCurrent == null )
- syncTip(null);
- var time = allocLabel(count++);
- time.x = xPos + 3;
- time.y = -1;
- time.visible = true;
- time.textColor = 0xFFFFFF;
- var timeMs = totalTime / 1e6;
- time.text = Std.int(timeMs) + "." + Std.int((timeMs * 10) % 10) + (measureCpu?" cpu" : " gpu");
- while( labels.length > count )
- labels.pop().remove();
- }
- function allocLabel(index) {
- var l = labels[index];
- if( l != null )
- return l;
- if( font == null ) font = hxd.res.DefaultFont.get();
- l = new h2d.Text(font, this);
- labels[index] = l;
- return l;
- }
- public function end() {
- if( !enable ) return;
- measure("end");
- waitFrames.push(currentFrame);
- currentFrame = null;
- }
- function allocStat( name, time : Float ) {
- var s = cachedStats;
- if( s != null )
- cachedStats = s.next;
- else
- s = new StatsObject();
- if( name == s.name ) {
- // smooth
- var et = hxd.Math.abs(time - s.time);
- if( et > recalTime )
- s.time = time;
- else
- s.time = s.time * smoothTime + time * (1 - smoothTime);
- } else {
- s.name = name;
- s.time = time;
- }
- s.next = currentStats;
- currentStats = s;
- return s;
- }
- function allocQuery() {
- var q = cachedQueries;
- if( q != null )
- cachedQueries = q.next;
- else
- q = new QueryObject();
- return q;
- }
- public function measure( name : String ) {
- if( !enable ) return;
- if( currentFrame != null && currentFrame.name == name )
- return;
- var q = allocQuery();
- q.name = name;
- q.drawCalls = engine.drawCalls;
- q.next = currentFrame;
- currentFrame = q;
- engine.driver.endQuery(q.q);
- if( measureCpu ) q.value = haxe.Timer.stamp() * 1e9;
- }
- public static function takeControl( app : hxd.App, ?s3d : h3d.scene.Scene ) @:privateAccess {
- if( s3d == null ) s3d = app.s3d;
- var cur = hxd.System.getCurrentLoop();
- var prevInt = s3d.interactives;
- var prevScenes = app.sevents.scenes;
- app.sevents.scenes = [s3d];
- s3d.interactives = [];
- var camCtrl = new h3d.scene.CameraController(s3d);
- var prevStates = new Map();
- var frustum = s3d.camera.frustum;
- function getRec( obj : h3d.scene.Object ) {
- if( obj.cullingCollider != null ) {
- prevStates.set(obj,{ cul : obj.cullingCollider, culled : obj.culled });
- obj.culled = obj.culled || !obj.cullingCollider.inFrustum(frustum);
- obj.cullingCollider = null;
- }
- for( o in obj )
- getRec(o);
- }
- getRec(s3d);
- s3d.ctx.debugCulling = true;
- camCtrl.loadFromCamera();
- if( app.s2d != null ) app.s2d.setElapsedTime(0);
- if( app.s3d != null ) app.s3d.setElapsedTime(0);
- hxd.System.setLoop(function() {
- app.sevents.checkEvents();
- app.engine.render(app);
- });
- }
- }
|