Index: binary-improvements/7dtd-server-fixes/src/FileCache/MapTileCache.cs
===================================================================
--- binary-improvements/7dtd-server-fixes/src/FileCache/MapTileCache.cs	(revision 328)
+++ binary-improvements/7dtd-server-fixes/src/FileCache/MapTileCache.cs	(revision 329)
@@ -2,4 +2,5 @@
 using System.IO;
 using UnityEngine;
+using UnityEngine.Profiling;
 using Object = UnityEngine.Object;
 
@@ -26,4 +27,7 @@
 		public void SetZoomCount (int count) {
 			cache = new CurrentZoomFile[count];
+			for (int i = 0; i < cache.Length; i++) {
+				cache [i] = new CurrentZoomFile ();
+			}
 		}
 
@@ -31,19 +35,23 @@
 			try {
 				lock (cache) {
-					if (cache [zoomlevel].filename == null || !cache [zoomlevel].filename.Equals (filename)) {
-						cache [zoomlevel].filename = filename;
+					CurrentZoomFile cacheEntry = cache [zoomlevel];
+					
+					if (cacheEntry.filename == null || !cacheEntry.filename.Equals (filename)) {
+						cacheEntry.filename = filename;
 
 						if (!File.Exists (filename)) {
-							cache [zoomlevel].data = null;
+							cacheEntry.pngData = null;
 							return null;
 						}
 
-						cache [zoomlevel].data = File.ReadAllBytes (filename);
+						Profiler.BeginSample ("ReadPng");
+						cacheEntry.pngData = File.ReadAllBytes (filename);
+						Profiler.EndSample ();
 					}
 
-					return cache [zoomlevel].data;
+					return cacheEntry.pngData;
 				}
 			} catch (Exception e) {
-				Log.Out ("Error in MapTileCache.LoadTile: " + e);
+				Log.Warning ("Error in MapTileCache.LoadTile: " + e);
 			}
 
@@ -51,16 +59,21 @@
 		}
 
-		public void SaveTile (int zoomlevel, byte[] content) {
+		public void SaveTile (int zoomlevel, byte[] contentPng) {
 			try {
 				lock (cache) {
-					if (cache [zoomlevel].filename == null) {
+					CurrentZoomFile cacheEntry = cache [zoomlevel];
+					
+					if (string.IsNullOrEmpty (cacheEntry.filename)) {
 						return;
 					}
+					
+					cacheEntry.pngData = contentPng;
 
-					cache [zoomlevel].data = content;
-					File.WriteAllBytes (cache [zoomlevel].filename, content);
+					Profiler.BeginSample ("WritePng");
+					File.WriteAllBytes (cacheEntry.filename, contentPng);
+					Profiler.EndSample ();
 				}
 			} catch (Exception e) {
-				Log.Out ("Error in MapTileCache.SaveTile: " + e);
+				Log.Warning ("Error in MapTileCache.SaveTile: " + e);
 			}
 		}
@@ -71,5 +84,5 @@
 					foreach (CurrentZoomFile czf in cache) {
 						if (czf.filename != null && czf.filename.Equals (filename)) {
-							return czf.data;
+							return czf.pngData;
 						}
 					}
@@ -82,5 +95,5 @@
 				}
 			} catch (Exception e) {
-				Log.Out ("Error in MapTileCache.GetFileContent: " + e);
+				Log.Warning ("Error in MapTileCache.GetFileContent: " + e);
 			}
 
@@ -88,7 +101,7 @@
 		}
 
-		private struct CurrentZoomFile {
+		private class CurrentZoomFile {
 			public string filename;
-			public byte[] data;
+			public byte[] pngData;
 		}
 	}
Index: binary-improvements/MapRendering/MapRendering/MapRenderBlockBuffer.cs
===================================================================
--- binary-improvements/MapRendering/MapRendering/MapRenderBlockBuffer.cs	(revision 328)
+++ binary-improvements/MapRendering/MapRendering/MapRenderBlockBuffer.cs	(revision 329)
@@ -2,31 +2,48 @@
 using System.IO;
 using AllocsFixes.FileCache;
+using Unity.Collections;
 using UnityEngine;
+using UnityEngine.Profiling;
 
 namespace AllocsFixes.MapRendering {
 	public class MapRenderBlockBuffer {
-		private readonly Texture2D blockMap = new Texture2D (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE);
+		private readonly Texture2D blockMap = new Texture2D (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE, TextureFormat.ARGB32, false);
 		private readonly MapTileCache cache;
 		private readonly Color nullColor = new Color (0, 0, 0, 0);
-		private readonly Texture2D zoomBuffer = new Texture2D (1, 1);
+		private readonly Texture2D zoomBuffer = new Texture2D (1, 1, TextureFormat.ARGB32, false);
 		private readonly int zoomLevel;
-		private string currentBlockMap = string.Empty;
+		private readonly string folderBase;
+		
+		private Vector2i currentBlockMapPos = new Vector2i (Int32.MinValue, Int32.MinValue);
+		private string currentBlockMapFolder = string.Empty;
+		private string currentBlockMapFilename = string.Empty;
 
 		public MapRenderBlockBuffer (int level, MapTileCache cache) {
 			zoomLevel = level;
 			this.cache = cache;
+			folderBase = Constants.MAP_DIRECTORY + "/" + zoomLevel + "/";
+		}
+
+		public TextureFormat FormatSelf {
+			get { return blockMap.format; }
+		}
+
+		public TextureFormat FormatScaled {
+			get { return zoomBuffer.format; }
 		}
 
 		public void ResetBlock () {
-			currentBlockMap = string.Empty;
+			currentBlockMapFolder = string.Empty;
+			currentBlockMapFilename = string.Empty;
+			currentBlockMapPos = new Vector2i (Int32.MinValue, Int32.MinValue);
 		}
 
 		public void SaveBlock () {
 			try {
-				if (currentBlockMap.Length > 0) {
-					saveTextureToFile (currentBlockMap);
+				if (currentBlockMapFilename.Length > 0) {
+					saveTextureToFile (currentBlockMapFilename);
 				}
 			} catch (Exception e) {
-				Log.Out ("Exception in MapRenderBlockBuffer.SaveBlock(): " + e);
+				Log.Warning ("Exception in MapRenderBlockBuffer.SaveBlock(): " + e);
 			}
 		}
@@ -35,14 +52,30 @@
 			bool res = false;
 			lock (blockMap) {
-				string folder = Constants.MAP_DIRECTORY + "/" + zoomLevel + "/" + block.x;
-				string fileName = folder + "/" + block.y + ".png";
-				Directory.CreateDirectory (folder);
-				if (!fileName.Equals (currentBlockMap)) {
-					res = true;
-					SaveBlock ();
-					loadTextureFromFile (fileName);
-				}
-
-				currentBlockMap = fileName;
+				if (currentBlockMapPos != block) {
+					Profiler.BeginSample ("LoadBlock.Strings");
+					string folder;
+					if (currentBlockMapPos.x != block.x) {
+						folder = folderBase + block.x;
+
+						Profiler.BeginSample ("LoadBlock.Directory");
+						Directory.CreateDirectory (folder);
+						Profiler.EndSample ();
+					} else {
+						folder = currentBlockMapFolder;
+					}
+
+					string fileName = folder + "/" + block.y + ".png";
+					Profiler.EndSample ();
+					
+					if (!fileName.Equals (currentBlockMapFilename)) {
+						res = true;
+						SaveBlock ();
+						loadTextureFromFile (fileName);
+					}
+
+					currentBlockMapFolder = folder;
+					currentBlockMapPos = block;
+					currentBlockMapFilename = fileName;
+				}
 			}
 
@@ -51,28 +84,107 @@
 
 		public void SetPart (Vector2i offset, int partSize, Color32[] pixels) {
-			if (offset.x + partSize > blockMap.width || offset.y + partSize > blockMap.height) {
+			if (offset.x + partSize > Constants.MAP_BLOCK_SIZE || offset.y + partSize > Constants.MAP_BLOCK_SIZE) {
 				Log.Error (string.Format ("MapBlockBuffer[{0}].SetPart ({1}, {2}, {3}) has blockMap.size ({4}/{5})",
-					zoomLevel, offset, partSize, pixels.Length, blockMap.width, blockMap.height));
+					zoomLevel, offset, partSize, pixels.Length, Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE));
 				return;
 			}
 
+			Profiler.BeginSample ("SetPart");
 			blockMap.SetPixels32 (offset.x, offset.y, partSize, partSize, pixels);
+			Profiler.EndSample ();
 		}
 
 		public Color32[] GetHalfScaled () {
+			Profiler.BeginSample ("HalfScaled.ResizeBuffer");
 			zoomBuffer.Resize (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE);
-			zoomBuffer.SetPixels32 (blockMap.GetPixels32 ());
-
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaled.CopyPixels");
+			if (blockMap.format == zoomBuffer.format) {
+				Profiler.BeginSample ("Native");
+				NativeArray<byte> dataSrc = blockMap.GetRawTextureData<byte> ();
+				NativeArray<byte> dataZoom = zoomBuffer.GetRawTextureData<byte> ();
+				dataSrc.CopyTo (dataZoom);
+				Profiler.EndSample ();
+			} else {
+				Profiler.BeginSample ("GetSetPixels");
+				zoomBuffer.SetPixels32 (blockMap.GetPixels32 ());
+				Profiler.EndSample ();
+			}
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaled.Scale");
 			TextureScale.Point (zoomBuffer, Constants.MAP_BLOCK_SIZE / 2, Constants.MAP_BLOCK_SIZE / 2);
-
-			return zoomBuffer.GetPixels32 ();
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaled.Return");
+			Color32[] result = zoomBuffer.GetPixels32 ();
+			Profiler.EndSample ();
+
+			return result;
+		}
+
+		public void SetPartNative (Vector2i offset, int partSize, NativeArray<int> pixels) {
+			if (offset.x + partSize > Constants.MAP_BLOCK_SIZE || offset.y + partSize > Constants.MAP_BLOCK_SIZE) {
+				Log.Error (string.Format ("MapBlockBuffer[{0}].SetPart ({1}, {2}, {3}) has blockMap.size ({4}/{5})",
+					zoomLevel, offset, partSize, pixels.Length, Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE));
+				return;
+			}
+
+			Profiler.BeginSample ("SetPartNative");
+			NativeArray<int> destData = blockMap.GetRawTextureData<int> ();
+			
+			for (int y = 0; y < partSize; y++) {
+				int srcLineStartIdx = partSize * y;
+				int destLineStartIdx = blockMap.width * (offset.y + y) + offset.x;
+				for (int x = 0; x < partSize; x++) {
+					destData [destLineStartIdx + x] = pixels [srcLineStartIdx + x];
+				}
+			}
+			Profiler.EndSample ();
+		}
+
+		public NativeArray<int> GetHalfScaledNative () {
+			Profiler.BeginSample ("HalfScaledNative.ResizeBuffer");
+			zoomBuffer.Resize (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE);
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaledNative.CopyPixels");
+			if (blockMap.format == zoomBuffer.format) {
+				Profiler.BeginSample ("Native");
+				NativeArray<byte> dataSrc = blockMap.GetRawTextureData<byte> ();
+				NativeArray<byte> dataZoom = zoomBuffer.GetRawTextureData<byte> ();
+				dataSrc.CopyTo (dataZoom);
+				Profiler.EndSample ();
+			} else {
+				Profiler.BeginSample ("GetSetPixels");
+				zoomBuffer.SetPixels32 (blockMap.GetPixels32 ());
+				Profiler.EndSample ();
+			}
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("HalfScaledNative.Scale");
+			TextureScale.Point (zoomBuffer, Constants.MAP_BLOCK_SIZE / 2, Constants.MAP_BLOCK_SIZE / 2);
+			Profiler.EndSample ();
+
+			return zoomBuffer.GetRawTextureData<int> ();
 		}
 
 		private void loadTextureFromFile (string _fileName) {
+			Profiler.BeginSample ("LoadTexture");
+
+			Profiler.BeginSample ("LoadFile");
 			byte[] array = cache.LoadTile (zoomLevel, _fileName);
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("LoadImage");
 			if (array != null && blockMap.LoadImage (array) && blockMap.height == Constants.MAP_BLOCK_SIZE &&
 			    blockMap.width == Constants.MAP_BLOCK_SIZE) {
+				Profiler.EndSample ();
+
+				Profiler.EndSample ();
 				return;
 			}
+			Profiler.EndSample ();
 
 			if (array != null) {
@@ -81,5 +193,5 @@
 
 			if (blockMap.height != Constants.MAP_BLOCK_SIZE || blockMap.width != Constants.MAP_BLOCK_SIZE) {
-				blockMap.Resize (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE);
+				blockMap.Resize (Constants.MAP_BLOCK_SIZE, Constants.MAP_BLOCK_SIZE, TextureFormat.ARGB32, false);
 			}
 
@@ -89,9 +201,16 @@
 				}
 			}
+			Profiler.EndSample ();
 		}
 
 		private void saveTextureToFile (string _fileName) {
+			Profiler.BeginSample ("SaveTexture");
+
+			Profiler.BeginSample ("EncodePNG");
 			byte[] array = blockMap.EncodeToPNG ();
+			Profiler.EndSample ();
+
 			cache.SaveTile (zoomLevel, array);
+			Profiler.EndSample ();
 		}
 	}
Index: binary-improvements/MapRendering/MapRendering/MapRendering.cs
===================================================================
--- binary-improvements/MapRendering/MapRendering/MapRendering.cs	(revision 328)
+++ binary-improvements/MapRendering/MapRendering/MapRendering.cs	(revision 329)
@@ -9,4 +9,5 @@
 using AllocsFixes.JSON;
 using UnityEngine;
+using UnityEngine.Profiling;
 using Object = UnityEngine.Object;
 
@@ -67,4 +68,5 @@
 		public static void RenderSingleChunk (Chunk chunk) {
 			if (renderingEnabled) {
+				// TODO: Replace with regular thread and a blocking queue / set
 				ThreadPool.UnsafeQueueUserWorkItem (o => {
 					try {
@@ -194,4 +196,6 @@
 			}
 		}
+		
+		private readonly WaitForSeconds coroutineDelay = new WaitForSeconds (0.2f);
 
 		private IEnumerator renderCoroutine () {
@@ -203,11 +207,16 @@
 
 					if (Time.time > renderTimeout || dirtyChunks.Count > 200) {
+						Profiler.BeginSample ("RenderDirtyChunks");
 						RenderDirtyChunks ();
-					}
-				}
-
-				yield return new WaitForSeconds (0.2f);
-			}
-		}
+						Profiler.EndSample ();
+					}
+				}
+
+				yield return coroutineDelay;
+			}
+		}
+
+		private readonly List<Vector2i> chunksToRender = new List<Vector2i> ();
+		private readonly List<Vector2i> chunksRendered = new List<Vector2i> ();
 
 		private void RenderDirtyChunks () {
@@ -218,9 +227,12 @@
 			}
 
-			List<Vector2i> keys = new List<Vector2i> (dirtyChunks.Keys);
-			List<Vector2i> chunksDone = new List<Vector2i> ();
-
-			Vector2i chunkPos = keys [0];
-			chunksDone.Add (chunkPos);
+			Profiler.BeginSample ("RenderDirtyChunks.Prepare");
+			chunksToRender.Clear ();
+			chunksRendered.Clear ();
+
+			dirtyChunks.CopyKeysTo (chunksToRender);
+
+			Vector2i chunkPos = chunksToRender [0];
+			chunksRendered.Add (chunkPos);
 
 			//Log.Out ("Start Dirty: " + chunkPos);
@@ -231,12 +243,14 @@
 
 			zoomLevelBuffers [Constants.ZOOMLEVELS - 1].LoadBlock (block);
-
+			Profiler.EndSample ();
+
+			Profiler.BeginSample ("RenderDirtyChunks.Work");
 			Vector2i v_block, v_blockOffset;
-			foreach (Vector2i v in keys) {
+			foreach (Vector2i v in chunksToRender) {
 				getBlockNumber (v, out v_block, out v_blockOffset, Constants.MAP_BLOCK_TO_CHUNK_DIV,
 					Constants.MAP_CHUNK_SIZE);
 				if (v_block.Equals (block)) {
 					//Log.Out ("Dirty: " + v + " render: true");
-					chunksDone.Add (v);
+					chunksRendered.Add (v);
 					if (dirtyChunks [v].Length != Constants.MAP_CHUNK_SIZE * Constants.MAP_CHUNK_SIZE) {
 						Log.Error (string.Format ("Rendering chunk has incorrect data size of {0} instead of {1}",
@@ -248,6 +262,7 @@
 				}
 			}
-
-			foreach (Vector2i v in chunksDone) {
+			Profiler.EndSample ();
+
+			foreach (Vector2i v in chunksRendered) {
 				dirtyChunks.Remove (v);
 			}
@@ -255,8 +270,11 @@
 			RenderZoomLevel (block);
 
+			Profiler.BeginSample ("RenderDirtyChunks.SaveAll");
 			SaveAllBlockMaps ();
+			Profiler.EndSample ();
 		}
 
 		private void RenderZoomLevel (Vector2i innerBlock) {
+			Profiler.BeginSample ("RenderZoomLevel");
 			int level = Constants.ZOOMLEVELS - 1;
 			while (level > 0) {
@@ -265,9 +283,19 @@
 
 				zoomLevelBuffers [level - 1].LoadBlock (block);
-				zoomLevelBuffers [level - 1].SetPart (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaled ());
-
-				level = level - 1;
+
+				Profiler.BeginSample ("RenderZoomLevel.Transfer");
+				if ((zoomLevelBuffers [level].FormatScaled == TextureFormat.ARGB32 ||
+				     zoomLevelBuffers [level].FormatScaled == TextureFormat.RGBA32) &&
+				    zoomLevelBuffers [level].FormatScaled == zoomLevelBuffers [level - 1].FormatSelf) {
+					zoomLevelBuffers [level - 1].SetPartNative (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaledNative ());
+				} else {
+					zoomLevelBuffers [level - 1].SetPart (blockOffset, Constants.MAP_BLOCK_SIZE / 2, zoomLevelBuffers [level].GetHalfScaled ());
+				}
+				Profiler.EndSample ();
+
+				level--;
 				innerBlock = block;
 			}
+			Profiler.EndSample ();
 		}
 
@@ -374,8 +402,4 @@
 		}
 
-		private static Color shortColorToColor (ushort col) {
-			return new Color (((col >> 10) & 31) / 31f, ((col >> 5) & 31) / 31f, (col & 31) / 31f, 255);
-		}
-
 		private static Color32 shortColorToColor32 (ushort col) {
 			byte r = (byte) (256 * ((col >> 10) & 31) / 32);
