Переглянути джерело

added threaded async texture loader

Nicolas Cannasse 3 роки тому
батько
коміт
0b393621ba
3 змінених файлів з 100 додано та 5 видалено
  1. 4 0
      h3d/mat/Data.hx
  2. 67 0
      hxd/impl/AsyncLoader.hx
  3. 29 5
      hxd/res/Image.hx

+ 4 - 0
h3d/mat/Data.hx

@@ -122,6 +122,10 @@ enum TextureFlags {
 		Tells if it's a texture array
 	**/
 	IsArray;
+	/**
+		Allows the texture to be loaded asynchronously (requires initializating hxd.res.Image.ASYNC_LOADER)
+	**/
+	AsyncLoading;
 }
 
 typedef TextureFormat = hxd.PixelFormat;

+ 67 - 0
hxd/impl/AsyncLoader.hx

@@ -0,0 +1,67 @@
+package hxd.impl;
+
+
+interface AsyncLoader {
+	public function load( img : hxd.res.Image ) : Void;
+}
+
+
+#if hl
+
+@:access(hxd.res.Image)
+class ThreadAsyncLoader implements AsyncLoader {
+
+	var deque : sys.thread.Deque<hxd.res.Image>;
+	var internalBuffer : haxe.io.Bytes;
+	var lock : sys.thread.Lock;
+	var currentTex : hxd.res.Image;
+
+	public function new() {
+		deque = new sys.thread.Deque();
+		lock = new sys.thread.Lock();
+		sys.thread.Thread.create(threadLoop);
+		haxe.MainLoop.add(uploadTextures);
+	}
+
+	function threadLoop() {
+		while( true ) {
+			var t = deque.pop(true);
+			if( t.tex == null || t.tex.isDisposed() ) continue;
+
+			var pos = 128;
+			var inf = t.inf;
+			if( inf.flags.has(Dxt10Header) ) pos += 20;
+			for( layer in 0...t.tex.layerCount ) {
+				for( mip in 0...inf.mipLevels ) {
+					var w = inf.width >> mip;
+					var h = inf.height >> mip;
+					if( w == 0 ) w = 1;
+					if( h == 0 ) h = 1;
+					var size = hxd.Pixels.calcDataSize(w, h, inf.pixelFormat);
+					pos += size;
+				}
+			}
+			var size = pos;
+			if( internalBuffer == null || internalBuffer.length < size )
+				internalBuffer = haxe.io.Bytes.alloc(size);
+			if( t.entry.readBytes(internalBuffer, 0, 0, size) != size )
+				throw "assert";
+
+			currentTex = t;
+			lock.wait();
+		}
+	}
+
+	function uploadTextures() {
+		if( currentTex == null ) return;
+		currentTex.asyncLoad(internalBuffer);
+		currentTex = null;
+		lock.release();
+	}
+
+	public function load(img) {
+		deque.add(img);
+	}
+
+}
+#end

+ 29 - 5
hxd/res/Image.hx

@@ -454,9 +454,33 @@ class Image extends Resource {
 		loadTexture();
 	}
 
-	function loadTexture() {
+	static var BLACK_1x1 = Pixels.alloc(1,1,RGBA);
+	public static var ASYNC_LOADER : hxd.impl.AsyncLoader;
+
+	function asyncLoad( data : haxe.io.Bytes ) {
+		if( tex == null || tex.isDisposed() ) return;
+		tex.dispose();
+		@:privateAccess {
+			tex.format = inf.pixelFormat;
+			tex.width = inf.width;
+			tex.height = inf.height;
+		}
+		loadTexture(data);
+	}
+
+	function loadTexture( ?asyncData:haxe.io.Bytes ) {
 		if( !getFormat().useAsyncDecode && !DEFAULT_ASYNC ) {
 			function load() {
+				if( inf.dataFormat == Dds && asyncData == null && tex.flags.has(AsyncLoading) ) @:privateAccess {
+					tex.format = RGBA;
+					tex.width = 1;
+					tex.height = 1;
+					tex.customMipLevels = 1;
+					tex.alloc();
+					tex.uploadPixels(BLACK_1x1);
+					ASYNC_LOADER.load(this);
+					return;
+				}
 				// immediately loading the PNG is faster than going through loadBitmap
 				@:privateAccess tex.customMipLevels = inf.mipLevels;
 				tex.alloc();
@@ -471,8 +495,8 @@ class Image extends Resource {
 							if( w == 0 ) w = 1;
 							if( h == 0 ) h = 1;
 							var size = hxd.Pixels.calcDataSize(w, h, inf.pixelFormat);
-							var bytes = entry.fetchBytes(pos, size);
-							tex.uploadPixels(new hxd.Pixels(w,h,bytes,inf.pixelFormat),mip,layer);
+							var bytes = asyncData == null ? entry.fetchBytes(pos, size) : asyncData;
+							tex.uploadPixels(new hxd.Pixels(w,h,bytes,inf.pixelFormat,asyncData == null ? 0 : pos),mip,layer);
 							pos += size;
 						}
 					}
@@ -485,7 +509,7 @@ class Image extends Resource {
 						}
 					}
 				}
-				tex.realloc = loadTexture;
+				tex.realloc = () -> loadTexture();
 				if(ENABLE_AUTO_WATCH)
 					watch(watchCallb);
 			}
@@ -501,7 +525,7 @@ class Image extends Resource {
 				tex.alloc();
 				tex.uploadBitmap(bmp);
 				bmp.dispose();
-				tex.realloc = loadTexture;
+				tex.realloc = () -> loadTexture();
 				tex.flags.unset(Loading);
 				@:privateAccess if( tex.waitLoads != null ) {
 					var arr = tex.waitLoads;