Просмотр исходного кода

review depth buffer management

Nicolas Cannasse 8 лет назад
Родитель
Сommit
045f271b95

+ 25 - 14
h3d/impl/Driver.hx

@@ -4,26 +4,32 @@ package h3d.impl;
 typedef IndexBuffer = flash.display3D.IndexBuffer3D;
 typedef VertexBuffer = Stage3dDriver.VertexWrapper;
 typedef Texture = flash.display3D.textures.TextureBase;
+typedef DepthBuffer = {};
 #elseif js
 typedef IndexBuffer = js.html.webgl.Buffer;
 typedef VertexBuffer = { b : js.html.webgl.Buffer, stride : Int };
-typedef Texture = { t : js.html.webgl.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, ?rb : js.html.webgl.Renderbuffer };
+typedef Texture = { t : js.html.webgl.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int };
+typedef DepthBuffer = { r : js.html.webgl.Renderbuffer };
 #elseif nme
 typedef IndexBuffer = nme.gl.GLBuffer;
 typedef VertexBuffer = { b : nme.gl.GLBuffer, stride : Int };
-typedef Texture = { t : nme.gl.GLTexture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, ?rb : nme.gl.GLRenderbuffer };
+typedef Texture = { t : nme.gl.GLTexture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int };
+typedef DepthBuffer = { r : nme.gl.Renderbuffer };
 #elseif lime
 typedef IndexBuffer = lime.graphics.opengl.GLBuffer;
 typedef VertexBuffer = { b : lime.graphics.opengl.GLBuffer, stride : Int };
-typedef Texture = { t : lime.graphics.opengl.GLTexture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, ?rb : lime.graphics.opengl.GLRenderbuffer };
+typedef Texture = { t : lime.graphics.opengl.GLTexture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int };
+typedef DepthBuffer = { r : lime.graphics.opengl.GLRenderbuffer };
 #elseif hxsdl
 typedef IndexBuffer = sdl.GL.Buffer;
 typedef VertexBuffer = { b : sdl.GL.Buffer, stride : Int };
-typedef Texture = { t : sdl.GL.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int, ?rb : sdl.GL.Renderbuffer };
+typedef Texture = { t : sdl.GL.Texture, width : Int, height : Int, internalFmt : Int, pixelFmt : Int };
+typedef DepthBuffer = { r : sdl.GL.Renderbuffer };
 #else
 typedef IndexBuffer = {};
 typedef VertexBuffer = {};
 typedef Texture = {};
+typedef DepthBuffer = {};
 #end
 
 enum Feature {
@@ -36,21 +42,15 @@ enum Feature {
 	*/
 	FloatTextures;
 	/*
-		Can we create a per-target-texture depth buffer.
+		Can we allocate custom depth buffers. If not, default depth buffer
+		(queried with DepthBuffer.getDefault()) will be clear if we change
+		the render target resolution or format.
 	*/
-	PerTargetDepthBuffer;
-	/*
-		Can we use the default depth buffer when rendering to a target texture.
-	*/
-	TargetUseDefaultDepthBuffer;
+	AllocDepthBuffer;
 	/*
 		Is our driver hardware accelerated or CPU emulated.
 	*/
 	HardwareAccelerated;
-	/*
-		Is it required to perform a full clear each frame on render target textures.
-	*/
-	FullClearRequired;
 	/*
 		Allows to render on several render targets with a single draw.
 	*/
@@ -140,6 +140,17 @@ class Driver {
 	public function setRenderTargets( textures : Array<h3d.mat.Texture> ) {
 	}
 
+	public function allocDepthBuffer( b : h3d.mat.DepthBuffer ) : DepthBuffer {
+		return null;
+	}
+
+	public function disposeDepthBuffer( b : h3d.mat.DepthBuffer ) {
+	}
+
+	public function getDefaultDepthBuffer() : h3d.mat.DepthBuffer {
+		return null;
+	}
+
 	public function present() {
 	}
 

+ 35 - 17
h3d/impl/GlDriver.hx

@@ -492,6 +492,13 @@ class GlDriver extends Driver {
 		bufferWidth = width;
 		bufferHeight = height;
 		gl.viewport(0, 0, width, height);
+
+		@:privateAccess if( defaultDepth != null ) {
+			disposeDepthBuffer(defaultDepth);
+			defaultDepth.width = this.bufferWidth;
+			defaultDepth.height = this.bufferHeight;
+			defaultDepth.b = allocDepthBuffer(defaultDepth);
+		}
 	}
 
 	function getChannels( t : Texture ) {
@@ -517,8 +524,6 @@ class GlDriver extends Driver {
 	}
 
 	override function allocTexture( t : h3d.mat.Texture ) : Texture {
-		if( t.flags.has(TargetUseDefaultDepth) )
-			throw "TargetUseDefaultDepth not supported in GL";
 		var tt = gl.createTexture();
 		var tt : Texture = { t : tt, width : t.width, height : t.height, internalFmt : GL.RGBA, pixelFmt : GL.UNSIGNED_BYTE };
 		switch( t.format ) {
@@ -550,16 +555,34 @@ class GlDriver extends Driver {
 				gl.texImage2D(CUBE_FACES[i], 0, tt.internalFmt, tt.width, tt.height, 0, getChannels(tt), tt.pixelFmt, null);
 		} else
 			gl.texImage2D(bind, 0, tt.internalFmt, tt.width, tt.height, 0, getChannels(tt), tt.pixelFmt, null);
-		if( t.flags.has(TargetDepth) ) {
-			tt.rb = gl.createRenderbuffer();
-			gl.bindRenderbuffer(GL.RENDERBUFFER, tt.rb);
-			gl.renderbufferStorage(GL.RENDERBUFFER, #if hl GL.DEPTH_COMPONENT24 #else GL.DEPTH_COMPONENT16 #end, tt.width, tt.height);
-			gl.bindRenderbuffer(GL.RENDERBUFFER, null);
-		}
 		gl.bindTexture(bind, null);
 		return tt;
 	}
 
+	override function allocDepthBuffer( b : h3d.mat.DepthBuffer ) : DepthBuffer {
+		var r = gl.createRenderbuffer();
+		gl.bindRenderbuffer(GL.RENDERBUFFER, r);
+		gl.renderbufferStorage(GL.RENDERBUFFER, #if hl GL.DEPTH_COMPONENT24 #else GL.DEPTH_COMPONENT16 #end, b.width, b.height);
+		gl.bindRenderbuffer(GL.RENDERBUFFER, null);
+		return { r : r };
+	}
+
+	override function disposeDepthBuffer( b : h3d.mat.DepthBuffer ) {
+		@:privateAccess if( b.b != null && b.b.r != null ) {
+			gl.deleteRenderbuffer(b.b.r);
+			b.b = null;
+		}
+	}
+
+	var defaultDepth : h3d.mat.DepthBuffer;
+
+	override function getDefaultDepthBuffer() : h3d.mat.DepthBuffer {
+		if( defaultDepth != null )
+			return defaultDepth;
+		defaultDepth = new h3d.mat.DepthBuffer(bufferWidth, bufferHeight);
+		return defaultDepth;
+	}
+
 	override function allocVertexes( m : ManagedBuffer ) : VertexBuffer {
 		var b = gl.createBuffer();
 		gl.bindBuffer(GL.ARRAY_BUFFER, b);
@@ -596,7 +619,6 @@ class GlDriver extends Driver {
 		if( tt == null ) return;
 		t.t = null;
 		gl.deleteTexture(tt.t);
-		if( tt.rb != null ) gl.deleteRenderbuffer(tt.rb);
 	}
 
 	override function disposeIndexes( i : IndexBuffer ) {
@@ -883,8 +905,8 @@ class GlDriver extends Driver {
 		tex.lastFrame = frame;
 		gl.bindFramebuffer(GL.FRAMEBUFFER, commonFB);
 		gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, tex.flags.has(Cubic) ? CUBE_FACES[face] : GL.TEXTURE_2D, tex.t.t, mipLevel);
-		if( tex.t.rb != null )
-			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, tex.t.rb);
+		if( tex.depthBuffer != null )
+			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, @:privateAccess tex.depthBuffer.b.r);
 		else
 			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, null);
 		gl.viewport(0, 0, tex.width >> mipLevel, tex.height >> mipLevel);
@@ -943,14 +965,10 @@ class GlDriver extends Driver {
 			false; // no support for glDrawBuffers in OpenFL
 			#end
 		#end
-		case PerTargetDepthBuffer:
-			true;
-		case TargetUseDefaultDepthBuffer:
-			false;
 		case HardwareAccelerated:
 			true;
-		case FullClearRequired:
-			false;
+		case AllocDepthBuffer:
+			true;
 		}
 	}
 

+ 18 - 7
h3d/impl/Stage3dDriver.hx

@@ -81,6 +81,7 @@ class Stage3dDriver extends Driver {
 	var isStandardMode : Bool;
 	var flashVersion : Float;
 	var tdisposed : Texture;
+	var defaultDepth : h3d.mat.DepthBuffer;
 
 	@:allow(h3d.impl.VertexWrapper)
 	var empty : flash.utils.ByteArray;
@@ -95,6 +96,7 @@ class Stage3dDriver extends Driver {
 		curSamplerBits = [];
 		curMultiBuffer = [];
 		defStencil = new Stencil();
+		defaultDepth = new h3d.mat.DepthBuffer( -1, -1);
 	}
 
 	override function logImpl( str : String ) {
@@ -164,9 +166,7 @@ class Stage3dDriver extends Driver {
 		return switch( f ) {
 		case HardwareAccelerated: ctx != null && ctx.driverInfo.toLowerCase().indexOf("software") == -1;
 		case StandardDerivatives, FloatTextures: isStandardMode;
-		case PerTargetDepthBuffer: false;
-		case TargetUseDefaultDepthBuffer: true;
-		case FullClearRequired: flashVersion < 15;
+		case AllocDepthBuffer: false;
 		case MultipleRenderTargets: (PROFILE == cast "standard") || (PROFILE == cast "standardExtended");
 		}
 	}
@@ -176,6 +176,10 @@ class Stage3dDriver extends Driver {
 			ctx.configureBackBuffer(width, height, antiAlias);
 			this.width = width;
 			this.height = height;
+			@:privateAccess {
+				defaultDepth.width = width;
+				defaultDepth.height = height;
+			}
 		} catch( e : flash.errors.Error ) {
 			// large screen but bad video card ?
 			if( width > 2048 || height > 2048 ) {
@@ -273,8 +277,6 @@ class Stage3dDriver extends Driver {
 	}
 
 	override function allocTexture( t : h3d.mat.Texture ) : Texture {
-		if( t.flags.has(TargetDepth) )
-			throw "TargetDepth not supported in Stage3D";
 		var fmt = switch( t.format ) {
 		case BGRA: flash.display3D.Context3DTextureFormat.BGRA;
 		case RGBA16F: flash.display3D.Context3DTextureFormat.RGBA_HALF_FLOAT;
@@ -776,6 +778,14 @@ class Stage3dDriver extends Driver {
 		}
 	}
 
+	override function allocDepthBuffer(b:h3d.mat.DepthBuffer):DepthBuffer {
+		throw "You can't allocate custom depth buffer on this platform.";
+	}
+
+	override function getDefaultDepthBuffer() {
+		return defaultDepth;
+	}
+
 	override function draw( ibuf : IndexBuffer, startIndex : Int, ntriangles : Int ) {
 		if( enableDraw ) {
 			if( ctx.enableErrorChecking )
@@ -822,7 +832,7 @@ class Stage3dDriver extends Driver {
 		} else {
 			if( t.t == null )
 				t.alloc();
-			ctx.setRenderToTexture(t.t, t.flags.has(TargetUseDefaultDepth), 0, face);
+			ctx.setRenderToTexture(t.t, t.depthBuffer != null, 0, face);
 			renderTargets = 1;
 			t.lastFrame = frame;
 			// make sure we at least clear the color the first time
@@ -839,11 +849,12 @@ class Stage3dDriver extends Driver {
 			setRenderTarget(null);
 			return;
 		}
+		var hasDepth = textures[0].depthBuffer != null;
 		for( i in 0...textures.length ) {
 			var t = textures[i];
 			if( t.t == null )
 				t.alloc();
-			ctx.setRenderToTexture(t.t, t.flags.has(TargetUseDefaultDepth), 0, 0, i);
+			ctx.setRenderToTexture(t.t, hasDepth, 0, 0, i);
 			t.lastFrame = frame;
 		}
 		for( i in textures.length...renderTargets )

+ 5 - 7
h3d/impl/TextureCache.hx

@@ -5,16 +5,14 @@ class TextureCache {
 	var cache : Array<h3d.mat.Texture>;
 	var position : Int = 0;
 	var frame : Int;
+	var defaultDepthBuffer : h3d.mat.DepthBuffer;
 	public var defaultFormat : hxd.PixelFormat;
-	public var hasDefaultDepth(default,null) : Bool;
-	public var fullClearRequired(default,null) : Bool;
 
 	public function new() {
 		cache = [];
 		var engine = h3d.Engine.getCurrent();
-		hasDefaultDepth = engine.driver.hasFeature(TargetUseDefaultDepthBuffer);
-		fullClearRequired = engine.driver.hasFeature(FullClearRequired);
 		defaultFormat = h3d.mat.Texture.nativeFormat;
+		defaultDepthBuffer = h3d.mat.DepthBuffer.getDefault();
 	}
 
 	public inline function get( index = 0 ) {
@@ -37,17 +35,17 @@ class TextureCache {
 		}
 	}
 
-	public function allocTarget( name : String, ctx : h3d.impl.RenderContext, width : Int, height : Int, hasDepth=true, ?format:hxd.PixelFormat ) {
+	public function allocTarget( name : String, ctx : h3d.impl.RenderContext, width : Int, height : Int, defaultDepth=true, ?format:hxd.PixelFormat ) {
 		begin(ctx);
 		var t = cache[position];
 		if( format == null ) format = defaultFormat;
-		if( t == null || t.isDisposed() || t.width != width || t.height != height || t.flags.has(hasDefaultDepth ? TargetUseDefaultDepth : TargetDepth) != hasDepth || t.format != format ) {
+		if( t == null || t.isDisposed() || t.width != width || t.height != height || t.format != format ) {
 			if( t != null ) t.dispose();
 			var flags : Array<h3d.mat.Data.TextureFlags> = [Target];
-			if( hasDepth ) flags.push(hasDefaultDepth ? TargetUseDefaultDepth : TargetDepth);
 			t = new h3d.mat.Texture(width, height, flags, format);
 			cache[position] = t;
 		}
+		t.depthBuffer = defaultDepth ? defaultDepthBuffer : null;
 		t.setName(name);
 		position++;
 		return t;

+ 0 - 8
h3d/mat/Data.hx

@@ -76,14 +76,6 @@ enum TextureFlags {
 		Allocate a texture that will be used as render target.
 	**/
 	Target;
-	/**
-		Add depth buffer on target texture
-	**/
-	TargetDepth;
-	/**
-		Use depth depth buffer when rendering to target texture
-	**/
-	TargetUseDefaultDepth;
 	/**
 		Allocate a cubic texture. Might be restricted to power of two textures only.
 	**/

+ 40 - 0
h3d/mat/DepthBuffer.hx

@@ -0,0 +1,40 @@
+package h3d.mat;
+
+/**
+	Depth buffer are used to store per pixel depth information when rendering a scene (also called Z-buffer)
+**/
+class DepthBuffer {
+
+	var b : h3d.impl.Driver.DepthBuffer;
+	public var width(default, null) : Int;
+	public var height(default, null) : Int;
+
+	/**
+		Creates a new depth buffer, it can be attached to one or several render target Texture by setting their `depthBuffer` property.
+	**/
+	public function new( width : Int, height : Int ) {
+		this.width = width;
+		this.height = height;
+		if( width >= 0 )
+			b = h3d.Engine.getCurrent().driver.allocDepthBuffer(this);
+	}
+
+	public function dispose() {
+		if( b != null ) {
+			h3d.Engine.getCurrent().driver.disposeDepthBuffer(this);
+			b = null;
+		}
+	}
+
+	public function isDisposed() {
+		return b == null;
+	}
+
+	/**
+		This will return the default depth buffer, which is automatically resized to the screen size.
+	**/
+	public static function getDefault() {
+		return h3d.Engine.getCurrent().driver.getDefaultDepthBuffer();
+	}
+
+}

+ 6 - 0
h3d/mat/Texture.hx

@@ -55,6 +55,12 @@ class Texture {
 	**/
 	public var realloc : Void -> Void;
 
+	/**
+		When the texture is used as render target, tells which depth buffer will be used.
+		If set to null, depth testing is disabled.
+	**/
+	public var depthBuffer : DepthBuffer;
+
 	public function new(w, h, ?flags : Array<TextureFlags>, ?format : TextureFormat, ?allocPos : h3d.impl.AllocPos ) {
 		#if !noEngine
 		var engine = h3d.Engine.getCurrent();

+ 6 - 2
h3d/pass/Blur.hx

@@ -101,21 +101,25 @@ class Blur extends ScreenFx<h3d.shader.Blur> {
 		if( depthBlur != null )
 			shader.cameraInverseViewProj = depthBlur.camera.getInverseViewProj();
 
+		var outDepth = output.depthBuffer;
+		var tmpDepth = tmp.depthBuffer;
+		output.depthBuffer = null;
+		tmp.depthBuffer = null;
 		for( i in 0...passes ) {
 			shader.texture = src;
 			shader.pixel.set(1 / src.width, 0);
 			engine.pushTarget(tmp);
-			if( fullClearRequired ) engine.clear(0, 1, 0);
 			render();
 			engine.popTarget();
 
 			shader.texture = tmp;
 			shader.pixel.set(0, 1 / tmp.height);
 			engine.pushTarget(output);
-			if( fullClearRequired ) engine.clear(0, 1, 0);
 			render();
 			engine.popTarget();
 		}
+		output.depthBuffer = outDepth;
+		tmp.depthBuffer = tmpDepth;
 
 		if( alloc )
 			tmp.dispose();

+ 9 - 1
h3d/pass/HardwarePick.hx

@@ -35,12 +35,20 @@ class HardwarePick extends Default {
 		super();
 		material = new h3d.mat.Pass("");
 		material.blend(One, Zero);
-		texOut = new h3d.mat.Texture(3, 3, [Target, TargetUseDefaultDepth]);
+		texOut = new h3d.mat.Texture(3, 3, [Target]);
+		#if !flash
+		texOut.depthBuffer = new h3d.mat.DepthBuffer(3, 3);
+		#else
+		texOut.depthBuffer = h3d.mat.DepthBuffer.getDefault();
+		#end
 	}
 
 	override function dispose() {
 		super.dispose();
 		texOut.dispose();
+		#if !flash
+		texOut.depthBuffer.dispose();
+		#end
 	}
 
 	override function getOutputs() {

+ 0 - 2
h3d/pass/ScreenFx.hx

@@ -7,7 +7,6 @@ class ScreenFx<T:hxsl.Shader> {
 	var manager : h3d.shader.Manager;
 	var plan : h3d.prim.Primitive;
 	var engine : h3d.Engine;
-	var fullClearRequired : Bool;
 	var shaders : hxsl.ShaderList;
 	var buffers : h3d.shader.Buffers;
 
@@ -20,7 +19,6 @@ class ScreenFx<T:hxsl.Shader> {
 		pass.depth(false, Always);
 		plan = h3d.prim.Plan2D.get();
 		engine = h3d.Engine.getCurrent();
-		fullClearRequired = engine.hasFeature(FullClearRequired);
 	}
 
 	public function setGlobals( ctx :  h3d.scene.RenderContext ) {

+ 11 - 2
h3d/pass/ShadowMap.hx

@@ -8,6 +8,8 @@ class ShadowMap extends Default {
 	var shadowColorId : Int;
 	var shadowPowerId : Int;
 	var shadowBiasId : Int;
+	var customDepth : Bool;
+	var depth : h3d.mat.DepthBuffer;
 	@ignore public var border : Border;
 	public var size(default,set) : Int;
 	public var color : h3d.Vector;
@@ -29,6 +31,8 @@ class ShadowMap extends Default {
 		color = new h3d.Vector();
 		blur = new Blur(2, 3);
 		border = new Border(size, size);
+		customDepth = h3d.Engine.getCurrent().driver.hasFeature(AllocDepthBuffer);
+		if( !customDepth ) depth = h3d.mat.DepthBuffer.getDefault();
 	}
 
 	function set_size(s) {
@@ -114,7 +118,12 @@ class ShadowMap extends Default {
 	}
 
 	override function draw( passes ) {
-		var texture = tcache.allocTarget("shadowMap", ctx, size, size);
+		var texture = tcache.allocTarget("shadowMap", ctx, size, size, false);
+		if( customDepth && (depth == null || depth.width != size || depth.height != size || depth.isDisposed()) ) {
+			if( depth != null ) depth.dispose();
+			depth = new h3d.mat.DepthBuffer(size, size);
+		}
+		texture.depthBuffer = depth;
 		var ct = ctx.camera.target;
 		var slight = ctx.lightSystem.shadowLight;
 		if( slight == null )
@@ -130,7 +139,7 @@ class ShadowMap extends Default {
 		lightCamera.update();
 
 		ctx.engine.pushTarget(texture);
-		ctx.engine.clear(0xFFFFFF, 1, tcache.fullClearRequired ? 0 : null);
+		ctx.engine.clear(0xFFFFFF, 1);
 		passes = super.draw(passes);
 		if( border != null ) border.render();
 		ctx.engine.popTarget();