浏览代码

started scn driver to record/replay whole scene render

Nicolas Cannasse 10 年之前
父节点
当前提交
8246098c91
共有 5 个文件被更改,包括 400 次插入4 次删除
  1. 5 0
      h3d/Engine.hx
  2. 241 0
      h3d/impl/ScnDriver.hx
  3. 19 4
      hxd/App.hx
  4. 34 0
      hxd/fmt/scn/Data.hx
  5. 101 0
      hxd/fmt/scn/Writer.hx

+ 5 - 0
h3d/Engine.hx

@@ -57,6 +57,11 @@ class Engine {
 		#end
 	}
 
+	public function setDriver(d) {
+		driver = d;
+		if( mem != null ) mem.driver = d;
+	}
+
 	public static inline function getCurrent() {
 		check();
 		return CURRENT;

+ 241 - 0
h3d/impl/ScnDriver.hx

@@ -0,0 +1,241 @@
+package h3d.impl;
+import h3d.impl.Driver;
+import hxd.fmt.scn.Data;
+
+class ScnDriver extends Driver {
+
+	var d : Driver;
+	var ops : Array<Operation>;
+	var savedShaders : Map<Int, Bool>;
+	var UID = 0;
+	var indexMap : Map<IndexBuffer, Int>;
+	var vertexMap : Map<VertexBuffer, Int>;
+	var vertexStride : Array<Int>;
+
+	public function new( driver : Driver ) {
+		this.d = driver;
+		ops = [];
+		logEnable = true;
+		savedShaders = new Map();
+		indexMap = new Map();
+		vertexMap = new Map();
+		vertexStride = [];
+	}
+
+	public function getDriver() {
+		return d;
+	}
+
+	public function getBytes() {
+		var d : Data = {
+			version : 1,
+			ops : ops,
+		};
+		return new hxd.fmt.scn.Writer().write(d);
+	}
+
+	override function logImpl( str : String ) {
+		//d.logImpl(str);
+		ops.push(Log(str));
+	}
+
+	override function hasFeature( f : Feature ) {
+		return d.hasFeature(f);
+	}
+
+	override function isDisposed() {
+		return d.isDisposed();
+	}
+
+	override function dispose() {
+		d.dispose();
+	}
+
+	override function begin( frame : Int ) {
+		ops.push(Begin);
+		d.begin(frame);
+	}
+
+	override function clear( ?color : h3d.Vector, ?depth : Float, ?stencil : Int ) {
+		ops.push(Clear(color, depth, stencil));
+		d.clear(color, depth, stencil);
+	}
+
+	override function setCapture( bmp : hxd.BitmapData, callb : Void -> Void ) {
+		d.setCapture(bmp, callb);
+	}
+
+	override function reset() {
+		ops.push(Reset);
+		d.reset();
+	}
+
+	override function getDriverName( details : Bool ) {
+		return d.getDriverName(details);
+	}
+
+	override function init( onCreate : Bool -> Void, forceSoftware = false ) {
+		d.init(function(b) {
+			onCreate(b);
+		},forceSoftware);
+	}
+
+	override function resize( width : Int, height : Int ) {
+		ops.push(Resize(width, height));
+		d.resize(width, height);
+	}
+
+
+	override function selectShader( shader : hxsl.RuntimeShader ) {
+		var hasData = savedShaders.get(shader.id);
+		if( hasData )
+			ops.push(SelectShader(shader.id, null));
+		else {
+			var s = new haxe.Serializer();
+			s.useCache = true;
+			s.serialize(shader);
+			ops.push(SelectShader(shader.id, haxe.io.Bytes.ofString(s.toString())));
+			savedShaders.set(shader.id, true);
+		}
+		return d.selectShader(shader);
+	}
+
+	override function getNativeShaderCode( shader ) {
+		return d.getNativeShaderCode(shader);
+	}
+
+	override function selectMaterial( pass : h3d.mat.Pass ) {
+		ops.push(Material(@:privateAccess pass.bits));
+		d.selectMaterial(pass);
+	}
+
+	override function uploadShaderBuffers( buffers : h3d.shader.Buffers, which : h3d.shader.Buffers.BufferKind ) {
+		switch( which ) {
+		case Globals:
+			ops.push(UploadShaderBuffers(true, buffers.vertex.globals.toArray(), buffers.fragment.globals.toArray()));
+		case Params:
+			ops.push(UploadShaderBuffers(false, buffers.vertex.params.toArray(), buffers.fragment.params.toArray()));
+		case Textures:
+			ops.push(UploadShaderTextures([for( t in buffers.vertex.tex ) t.id], [for( t in buffers.fragment.tex ) t.id]));
+		}
+		d.uploadShaderBuffers(buffers, which);
+	}
+
+	override function getShaderInputNames() : Array<String> {
+		return d.getShaderInputNames();
+	}
+
+	override function selectBuffer( buffer : Buffer ) {
+		d.selectBuffer(buffer);
+	}
+
+	override function selectMultiBuffers( buffers : Buffer.BufferOffset ) {
+		d.selectMultiBuffers(buffers);
+	}
+
+	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
+		d.draw(ibuf, startIndex, ntriangles);
+	}
+
+	override function setRenderZone( x : Int, y : Int, width : Int, height : Int ) {
+		d.setRenderZone(x, y, width, height);
+	}
+
+	override function setRenderTarget( tex : Null<h3d.mat.Texture> ) {
+		d.setRenderTarget(tex);
+		ops.push(RenderTarget(tex == null ? -1 : tex.id));
+	}
+
+	override function present() {
+		d.present();
+		ops.push(Present);
+	}
+
+	override function setDebug( b : Bool ) {
+		d.setDebug(b);
+	}
+
+	override function allocTexture( t : h3d.mat.Texture ) : Texture {
+		ops.push(AllocTexture(t.id, t.name, t.width, t.height, t.flags));
+		return d.allocTexture(t);
+	}
+
+	override function allocIndexes( count : Int ) : IndexBuffer {
+		var ibuf = d.allocIndexes(count);
+		var id = ++UID;
+		indexMap.set(ibuf, id);
+		ops.push(AllocIndexes(id, count));
+		return ibuf;
+	}
+
+	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
+		var vbuf = d.allocVertexes(m);
+		var id = ++UID;
+		vertexMap.set(vbuf, id);
+		ops.push(AllocVertexes(id, m.size, m.stride, m.flags));
+		vertexStride[id] = m.stride;
+		return vbuf;
+	}
+
+	override function disposeTexture( t : h3d.mat.Texture ) {
+		d.disposeTexture(t);
+		ops.push(DisposeTexture(t.id));
+	}
+
+	override function disposeIndexes( i : IndexBuffer ) {
+		d.disposeIndexes(i);
+		ops.push(DisposeIndexes(indexMap.get(i)));
+		indexMap.remove(i);
+	}
+
+	override function disposeVertexes( v : VertexBuffer ) {
+		d.disposeVertexes(v);
+		ops.push(DisposeVertexes(vertexMap.get(v)));
+		vertexMap.remove(v);
+	}
+
+	override function uploadIndexBuffer( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : hxd.IndexBuffer, bufPos : Int ) {
+		d.uploadIndexBuffer(i, startIndice, indiceCount, buf, bufPos);
+		var bytes = haxe.io.Bytes.alloc(indiceCount * 2);
+		for( i in 0...indiceCount )
+			bytes.setUInt16(i << 1, buf[bufPos + i]);
+		ops.push(UploadIndexes(indexMap.get(i), startIndice, indiceCount, bytes));
+	}
+
+	override function uploadIndexBytes( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : haxe.io.Bytes , bufPos : Int ) {
+		d.uploadIndexBytes(i, startIndice, indiceCount, buf, bufPos);
+		ops.push(UploadIndexes(indexMap.get(i), startIndice, indiceCount, buf.sub(bufPos, indiceCount * 2)));
+	}
+
+	override function uploadVertexBuffer( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
+		d.uploadVertexBuffer(v, startVertex, vertexCount, buf, bufPos);
+		var stride = vertexStride[vertexMap.get(v)];
+		var bytes = haxe.io.Bytes.alloc(stride * vertexCount * 4);
+		for( i in 0...vertexCount * stride )
+			bytes.setFloat(i << 2, buf[i + bufPos]);
+		ops.push(UploadVertexes(vertexMap.get(v), startVertex, vertexCount, bytes));
+	}
+
+	override function uploadVertexBytes( v : VertexBuffer, startVertex : Int, vertexCount : Int, buf : haxe.io.Bytes, bufPos : Int ) {
+		d.uploadVertexBytes(v, startVertex, vertexCount, buf, bufPos);
+		var stride = vertexStride[vertexMap.get(v)];
+		ops.push(UploadVertexes(vertexMap.get(v), startVertex, vertexCount, buf.sub(bufPos, stride * vertexCount * 4)));
+	}
+
+	override function uploadTextureBitmap( t : h3d.mat.Texture, bmp : hxd.BitmapData, mipLevel : Int, side : Int ) {
+		d.uploadTextureBitmap(t, bmp, mipLevel, side);
+		var pixels = bmp.getPixels();
+		if( pixels.bytes.length != bmp.width * bmp.height * 4 )
+			pixels.bytes = pixels.bytes.sub(0, bmp.width * bmp.height * 4);
+		ops.push(UploadTexture(t.id, pixels, mipLevel, side));
+	}
+
+	override function uploadTexturePixels( t : h3d.mat.Texture, pixels : hxd.Pixels, mipLevel : Int, side : Int ) {
+		d.uploadTexturePixels(t, pixels, mipLevel, side);
+		var pixels = pixels.clone();
+		if( pixels.bytes.length != pixels.width * pixels.height * 4 )
+			pixels.bytes = pixels.bytes.sub(0, pixels.width * pixels.height * 4);
+		ops.push(UploadTexture(t.id, pixels, mipLevel, side));
+	}
+
+}

+ 19 - 4
hxd/App.hx

@@ -53,18 +53,33 @@ class App {
 			var old = driver.logEnable;
 			var log = new h3d.impl.LogDriver(driver);
 			log.logLines = [];
-			@:privateAccess engine.driver = log;
+			engine.setDriver(log);
 			try {
 				engine.render(s3d);
 			} catch( e : Dynamic ) {
 				log.logLines.push(Std.string(e));
 			}
 			driver.logEnable = old;
-			@:privateAccess engine.driver = driver;
+			engine.setDriver(driver);
 			hxd.File.saveBytes("log.txt", haxe.io.Bytes.ofString(log.logLines.join("\n")));
-		} else
-		#end
+		} else {
+			var scnDriver = Std.instance(engine.driver, h3d.impl.ScnDriver);
+			if( hxd.Key.isDown(hxd.Key.CTRL) && hxd.Key.isDown(hxd.Key.F11) ) {
+				if( scnDriver == null ) {
+					engine.setDriver(new h3d.impl.ScnDriver(engine.driver));
+					engine.mem.onContextLost();
+					engine.onContextLost();
+					engine.resize(engine.width, engine.height);
+				}
+			} else if( scnDriver != null ) {
+				engine.setDriver(scnDriver.getDriver());
+				hxd.File.saveBytes("record.scn", scnDriver.getBytes());
+			}
 			engine.render(s3d);
+		}
+		#else
+			engine.render(s3d);
+		#end
 	}
 
 	function update( dt : Float ) {

+ 34 - 0
hxd/fmt/scn/Data.hx

@@ -0,0 +1,34 @@
+package hxd.fmt.scn;
+
+typedef Serialized<T> = haxe.io.Bytes;
+
+enum Operation {
+	Log( str : String );
+	Begin;
+	Clear( ?color : h3d.Vector, ?depth : Float, ?stencil : Int );
+	Reset;
+	Resize( width : Int, height : Int );
+	SelectShader( id : Int, ?data : Serialized<hxsl.RuntimeShader> );
+	Material( bits : Int );
+	UploadShaderBuffers( globals : Bool, vertex : Array<Float>, fragment : Array<Float> );
+	UploadShaderTextures( vertex : Array<Int>, fragment : Array<Int> );
+
+	AllocTexture( id : Int, name : String, width : Int, height : Int, flags : haxe.EnumFlags<h3d.mat.Data.TextureFlags> );
+	AllocIndexes( id : Int, count : Int );
+	AllocVertexes( id : Int, stride : Int, count : Int, flags : haxe.EnumFlags<h3d.Buffer.BufferFlag> );
+	DisposeTexture( id : Int );
+	DisposeIndexes( id : Int );
+	DisposeVertexes( id : Int );
+
+	UploadTexture( id : Int, pixels : hxd.Pixels, mipMap : Int, side : Int );
+	UploadIndexes( id : Int, start : Int, count : Int, data : haxe.io.Bytes );
+	UploadVertexes( id : Int, start : Int, count : Int, data : haxe.io.Bytes );
+
+	RenderTarget( tid : Int );
+	Present;
+}
+
+typedef Data = {
+	var version : Int;
+	var ops : Array<Operation>;
+}

+ 101 - 0
hxd/fmt/scn/Writer.hx

@@ -0,0 +1,101 @@
+package hxd.fmt.scn;
+import hxd.fmt.scn.Data;
+
+class Writer {
+
+	var out : haxe.io.BytesBuffer;
+
+	public function new() {
+		out = new haxe.io.BytesBuffer();
+	}
+
+	public function write( d : Data ) {
+		out.addByte(d.version);
+		for( op in d.ops ) {
+			out.addByte(op.getIndex());
+			switch( op ) {
+			case Log(str):
+				out.addInt32(str.length);
+				out.addString(str);
+			case Begin, Reset, Present:
+			case Clear(color, depth, stencil):
+				out.addByte((color == null ? 0 : 1) | (depth == null ? 0 : 2) | (stencil == null ? 0 : 4));
+				if( color != null ) {
+					out.addFloat(color.r);
+					out.addFloat(color.g);
+					out.addFloat(color.b);
+					out.addFloat(color.a);
+				}
+				if( depth != null )
+					out.addFloat(depth);
+				if( stencil != null )
+					out.addInt32(stencil);
+			case Resize(w, h):
+				out.addInt32(w);
+				out.addInt32(h);
+			case SelectShader(id, data):
+				out.addInt32(id);
+				if( data != null ) out.addBytes(data, 0, data.length);
+			case Material(bits):
+				out.addInt32(bits);
+			case UploadShaderTextures(vertex, fragment):
+				out.addByte(vertex.length);
+				for( t in vertex )
+					out.addInt32(t);
+				out.addByte(fragment.length);
+				for( t in fragment )
+					out.addInt32(t);
+			case UploadShaderBuffers(g, vertex, fragment):
+				out.addByte(g?1:0);
+				out.addInt32(vertex.length);
+				for( v in vertex )
+					out.addFloat(v);
+				out.addInt32(fragment.length);
+				for( v in fragment )
+					out.addFloat(v);
+			case AllocTexture(id, name, w, h, flags):
+				out.addInt32(id);
+				if( name == null )
+					out.addInt32( -1);
+				else {
+					out.addInt32(name.length);
+					out.addString(name);
+				}
+				out.addInt32(w);
+				out.addInt32(h);
+				out.addInt32(flags.toInt());
+			case AllocIndexes(id, count):
+				out.addInt32(id);
+				out.addInt32(count);
+			case AllocVertexes(id, stride, count, flags):
+				out.addInt32(id);
+				out.addInt32(stride);
+				out.addInt32(count);
+				out.addInt32(flags.toInt());
+			case UploadTexture(id, pixels, mipMap, side):
+				out.addInt32(id);
+				out.addInt32(pixels.width);
+				out.addInt32(pixels.height);
+				out.addInt32(pixels.format.getIndex());
+				out.addInt32(pixels.flags.toInt());
+				out.add(pixels.bytes);
+			case UploadVertexes(id, start, count, data):
+				out.addInt32(id);
+				out.addInt32(start);
+				out.addInt32(count);
+				out.addInt32(data.length);
+				out.add(data);
+			case UploadIndexes(id, start, count, data):
+				out.addInt32(id);
+				out.addInt32(start);
+				out.addInt32(count);
+				out.addInt32(data.length);
+				out.add(data);
+			case RenderTarget(id), DisposeIndexes(id), DisposeTexture(id), DisposeVertexes(id):
+				out.addInt32(id);
+			}
+		}
+		return out.getBytes();
+	}
+
+}