瀏覽代碼

handle onContextLost in DirectX (dxgi_device_removed)

ncannasse 7 年之前
父節點
當前提交
0d8c8b8149
共有 2 個文件被更改,包括 92 次插入14 次删除
  1. 88 14
      h3d/impl/DirectXDriver.hx
  2. 4 0
      h3d/impl/MemoryManager.hx

+ 88 - 14
h3d/impl/DirectXDriver.hx

@@ -70,6 +70,8 @@ class DirectXDriver extends h3d.impl.Driver {
 	var driver : DriverInstance;
 	var shaders : Map<Int,CompiledShader>;
 
+	var hasDeviceError = false;
+
 	var defaultTarget : RenderTargetView;
 	var defaultDepth : DepthBuffer;
 	var defaultDepthInst : h3d.mat.DepthBuffer;
@@ -84,18 +86,18 @@ class DirectXDriver extends h3d.impl.Driver {
 	var currentIndex : IndexBuffer;
 	var currentDepth : DepthBuffer;
 	var currentTargets = new hl.NativeArray<RenderTargetView>(16);
-	var vertexShader = new PipelineState(Vertex);
-	var pixelShader = new PipelineState(Pixel);
+	var vertexShader : PipelineState;
+	var pixelShader : PipelineState;
 	var currentVBuffers = new hl.NativeArray<dx.Resource>(16);
 	var frame : Int;
 	var currentMaterialBits = -1;
 	var targetsCount = 1;
 	var allowDraw = false;
 
-	var depthStates : Map<Int,DepthStencilState> = new Map();
-	var blendStates : Map<Int,BlendState> = new Map();
-	var rasterStates : Map<Int,RasterState> = new Map();
-	var samplerStates : Map<Int,SamplerState> = new Map();
+	var depthStates : Map<Int,DepthStencilState>;
+	var blendStates : Map<Int,BlendState>;
+	var rasterStates : Map<Int,RasterState>;
+	var samplerStates : Map<Int,SamplerState>;
 	var currentDepthState : DepthStencilState;
 	var currentBlendState : BlendState;
 	var currentRasterState : RasterState;
@@ -111,13 +113,40 @@ class DirectXDriver extends h3d.impl.Driver {
 
 	var mapCount : Int;
 	var updateResCount : Int;
+	var onContextLost : Void -> Void;
 
 	public var backBufferFormat : dx.Format = R8G8B8A8_UNORM;
 	public var depthStencilFormat : dx.Format = D24_UNORM_S8_UINT;
 
 	public function new() {
-		shaders = new Map();
 		window = @:privateAccess dx.Window.windows[0];
+		Driver.setErrorHandler(onDXError);
+		reset();
+	}
+
+	function reset() {
+		allowDraw = false;
+		targetsCount = 1;
+		currentMaterialBits = -1;
+		if( shaders != null ) {
+			for( s in shaders ) {
+				s.fragment.shader.release();
+				s.vertex.shader.release();
+				s.layout.release();
+			}
+		}
+		if( depthStates != null ) for( s in depthStates ) if( s != null ) s.release();
+		if( blendStates != null ) for( s in blendStates ) if( s != null ) s.release();
+		if( rasterStates != null ) for( s in rasterStates ) if( s != null ) s.release();
+		if( samplerStates != null ) for( s in samplerStates ) if( s != null ) s.release();
+		shaders = new Map();
+		depthStates = new Map();
+		blendStates = new Map();
+		rasterStates = new Map();
+		samplerStates = new Map();
+		vertexShader = new PipelineState(Vertex);
+		pixelShader = new PipelineState(Pixel);
+
 		var options : dx.Driver.DriverInitFlags = None;
 		#if debug
 		options |= DebugLayer;
@@ -126,7 +155,7 @@ class DirectXDriver extends h3d.impl.Driver {
 		try
 			driver = Driver.create(window, backBufferFormat, options)
 		catch( e : Dynamic )
-			throw "Failed to initialize DirectX driver (" + e+")";
+			throw "Failed to initialize DirectX driver (" + e + ")";
 
 		if( driver == null ) throw "Failed to initialize DirectX driver";
 
@@ -143,6 +172,18 @@ class DirectXDriver extends h3d.impl.Driver {
 			blendFactors[i] = 0;
 	}
 
+	override function dispose() {
+		Driver.disposeDriver(driver);
+		driver = null;
+	}
+
+	function onDXError(code:Int,reason:Int,line:Int) {
+		if( code != 0x887A0005 /*DXGI_ERROR_DEVICE_REMOVED*/ )
+			throw "DXError "+StringTools.hex(code)+" line "+line;
+		//if( !hasDeviceError ) trace("DX_REMOVED "+StringTools.hex(reason)+":"+line);
+		hasDeviceError = true;
+	}
+
 	override function resize(width:Int, height:Int)  {
 		if( defaultDepth != null ) {
 			defaultDepth.view.release();
@@ -197,10 +238,11 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	override function isDisposed() {
-		return false;
+		return hasDeviceError;
 	}
 
 	override function init( onCreate : Bool -> Void, forceSoftware = false ) {
+		onContextLost = onCreate.bind(true);
 		haxe.Timer.delay(onCreate.bind(false), 1);
 	}
 
@@ -219,12 +261,25 @@ class DirectXDriver extends h3d.impl.Driver {
 		return desc;
 	}
 
+	public function forceDeviceError() {
+		hasDeviceError = true;
+	}
+
 	override function present() {
 		if( defaultTarget == null ) return;
 		var old = hxd.System.allowTimeout;
 		if( old ) hxd.System.allowTimeout = false;
 		Driver.present(window.vsync ? 1 : 0, None);
 		if( old ) hxd.System.allowTimeout = true;
+
+		if( hasDeviceError ) {
+			//trace("OnContextLost");
+			hasDeviceError = false;
+			dispose();
+			reset();
+			onContextLost();
+		}
+
 	}
 
 	override function getDefaultDepthBuffer():h3d.mat.DepthBuffer {
@@ -331,8 +386,8 @@ class DirectXDriver extends h3d.impl.Driver {
 		var tt = t.t;
 		if( tt == null ) return;
 		t.t = null;
-		tt.view.release();
-		tt.res.release();
+		if( tt.view != null ) tt.view.release();
+		if( tt.res != null ) tt.res.release();
 		if( tt.rt != null )
 			for( rt in tt.rt )
 				if( rt != null )
@@ -348,6 +403,7 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	override function generateMipMaps(texture:h3d.mat.Texture) {
+		if( hasDeviceError ) return;
 		Driver.generateMips(texture.t.view);
 	}
 
@@ -363,18 +419,22 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	override function uploadIndexBuffer(i:IndexBuffer, startIndice:Int, indiceCount:Int, buf:hxd.IndexBuffer, bufPos:Int) {
+		if( hasDeviceError ) return;
 		updateBuffer(i.res, hl.Bytes.getArray(buf.getNative()).offset(bufPos << 1), startIndice << 1, indiceCount << 1);
 	}
 
 	override function uploadIndexBytes(i:IndexBuffer, startIndice:Int, indiceCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
+		if( hasDeviceError ) return;
 		updateBuffer(i.res, @:privateAccess buf.b.offset(bufPos << 1), startIndice << 1, indiceCount << 1);
 	}
 
 	override function uploadVertexBuffer(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:hxd.FloatBuffer, bufPos:Int) {
+		if( hasDeviceError ) return;
 		updateBuffer(v.res, hl.Bytes.getArray(buf.getNative()).offset(bufPos<<2), startVertex * v.stride << 2, vertexCount * v.stride << 2);
 	}
 
 	override function uploadVertexBytes(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
+		if( hasDeviceError ) return;
 		updateBuffer(v.res, @:privateAccess buf.b.offset(bufPos << 2), startVertex * v.stride << 2, vertexCount * v.stride << 2);
 	}
 
@@ -441,6 +501,7 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	override function uploadTextureBitmap(t:h3d.mat.Texture, bmp:hxd.BitmapData, mipLevel:Int, side:Int) {
+		if( hasDeviceError ) return;
 		var pixels = bmp.getPixels();
 		uploadTexturePixels(t, pixels, mipLevel, side);
 		pixels.dispose();
@@ -449,6 +510,7 @@ class DirectXDriver extends h3d.impl.Driver {
 	override function uploadTexturePixels(t:h3d.mat.Texture, pixels:hxd.Pixels, mipLevel:Int, side:Int) {
 		pixels.convert(RGBA);
 		pixels.setFlip(false);
+		if( hasDeviceError ) return;
 		if( mipLevel >= t.t.mips ) throw "Mip level outside texture range : " + mipLevel + " (max = " + (t.t.mips - 1) + ")";
 		t.t.res.updateSubresource(mipLevel + side * t.t.mips, null, (pixels.bytes:hl.Bytes).offset(pixels.offset), pixels.width * hxd.Pixels.bytesPerPixel(pixels.format), 0);
 		updateResCount++;
@@ -561,8 +623,10 @@ class DirectXDriver extends h3d.impl.Driver {
 		if( compileOnly )
 			return { s : null, bytes : bytes };
 		var s = shader.vertex ? Driver.createVertexShader(bytes) : Driver.createPixelShader(bytes);
-		if( s == null )
+		if( s == null ) {
+			if( hasDeviceError ) return null;
 			throw "Failed to create shader\n" + shader.code;
+		}
 
 		var ctx = new ShaderContext(s);
 		ctx.globalsSize = shader.globalsSize;
@@ -645,6 +709,8 @@ class DirectXDriver extends h3d.impl.Driver {
 			setRenderTarget(null);
 			return;
 		}
+		if( hasDeviceError )
+			return;
 		var tex = textures[0];
 		curTexture = textures[0];
 		if( tex.depthBuffer != null && (tex.depthBuffer.width != tex.width || tex.depthBuffer.height != tex.height) )
@@ -652,8 +718,10 @@ class DirectXDriver extends h3d.impl.Driver {
 		currentDepth = @:privateAccess (tex.depthBuffer == null ? null : tex.depthBuffer.b);
 		for( i in 0...textures.length ) {
 			var tex = textures[i];
-			if( tex.t == null )
+			if( tex.t == null ) {
 				tex.alloc();
+				if( hasDeviceError ) return;
+			}
 			if( tex.t.rt == null )
 				throw "Can't render to texture which is not allocated with Target flag";
 			var index = mipLevel * 6 + face;
@@ -713,8 +781,10 @@ class DirectXDriver extends h3d.impl.Driver {
 		if( s == null ) {
 			s = new CompiledShader();
 			var vertex = compileShader(shader.vertex);
+			var fragment = compileShader(shader.fragment);
+			if( hasDeviceError ) return false;
 			s.vertex = vertex.s;
-			s.fragment = compileShader(shader.fragment).s;
+			s.fragment = fragment.s;
 			s.inputs = [];
 			s.offsets = [];
 
@@ -771,6 +841,7 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	override function selectBuffer(buffer:Buffer) {
+		if( hasDeviceError ) return;
 		var vbuf = @:privateAccess buffer.buffer.vbuf;
 		var start = -1, max = -1, position = 0;
 		for( i in 0...currentShader.inputs.length ) {
@@ -787,6 +858,7 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	override function selectMultiBuffers(bl:Buffer.BufferOffset) {
+		if( hasDeviceError ) return;
 		var index = 0;
 		var start = -1, max = -1;
 		while( bl != null ) {
@@ -806,6 +878,7 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	override function uploadShaderBuffers(buffers:h3d.shader.Buffers, which:h3d.shader.Buffers.BufferKind) {
+		if( hasDeviceError ) return;
 		uploadBuffers(vertexShader, currentShader.vertex, buffers.vertex, which);
 		uploadBuffers(pixelShader, currentShader.fragment, buffers.fragment, which);
 	}
@@ -870,6 +943,7 @@ class DirectXDriver extends h3d.impl.Driver {
 				if( t != null && t.t == null && t.realloc != null ) {
 					t.alloc();
 					t.realloc();
+					if( hasDeviceError ) return;
 				}
 				t.lastFrame = frame;
 

+ 4 - 0
h3d/impl/MemoryManager.hx

@@ -88,6 +88,9 @@ class MemoryManager {
 		if( mem == 0 ) return;
 
 		while( usedMemory + mem > MAX_MEMORY || bufferCount >= MAX_BUFFERS || (m.vbuf = driver.allocVertexes(m)) == null ) {
+
+			if( driver.isDisposed() ) return;
+
 			var size = usedMemory - freeMemorySize();
 			garbage();
 			cleanManagedBuffers();
@@ -253,6 +256,7 @@ class MemoryManager {
 		var free = cleanTextures(false);
 		t.t = driver.allocTexture(t);
 		if( t.t == null ) {
+			if( driver.isDisposed() ) return;
 			if( !cleanTextures(true) ) throw "Maximum texture memory reached";
 			allocTexture(t);
 			return;