Refactor Maptile fetcher into MapService.cs

This commit is contained in:
alexiscatnip
2022-01-22 02:40:52 +08:00
parent a6d79cd960
commit a045c9deb4
15 changed files with 212 additions and 151 deletions
+37 -16
View File
@@ -5,36 +5,57 @@ using Raindrop.Map.Model;
using Raindrop.ServiceLocator;
using UnityEngine;
// a simple view to get a maptile from the map service.
public class FetchMapTile : MonoBehaviour
{
public int Grid_X = 1000;
public int Grid_Y = 1000;
private bool modified = true;
private bool _needsTextureRefresh = true;
public TexturePlane image;
public MapTile MapTile;
// Update is called once per frame
void Start()
{
SyncMapTile_blocking(image, Grid_X, Grid_Y);
modified = false;
MapTile = RetrieveMapTile_blocking(image, Grid_X, Grid_Y);
_needsTextureRefresh = true;
}
// todo: refactor into a routine
void update()
{
if (_needsTextureRefresh == false)
{
return;
}
lock (MapTile)
{
if (MapTile.isReady)
{
image.ApplyTexture(MapTile.getTex());
}
}
}
//blocks until tex is successfully printed.
private void SyncMapTile_blocking(TexturePlane texturePlane, int gridX, int gridY)
private MapTile RetrieveMapTile_blocking(TexturePlane texturePlane, int gridX, int gridY)
{
MapFetcher mf = new MapFetcher();
MapService mapService = new MapService();
MapTile mt = mf.GetRegionTileExternal(Utils.UIntsToLong(1000,1000), 1);
while (mt.isReady == false)
{
continue;
}
lock (mt.getTex())
{
texturePlane.ApplyTexture(mt.getTex());
}
bool isReady;
MapTile mt = mapService.GetMapTile(Utils.UIntsToLong(1000,1000), 1, out isReady);
return mt;
// while (mt.isReady == false)
// {
// continue;
// }
//
// lock (mt.getTex())
// {
// texturePlane.ApplyTexture(mt.getTex());
// }
}
}
+3 -2
View File
@@ -385,8 +385,9 @@ namespace Raindrop.Rendering
{
if (state == TextureRequestState.Finished && assetTexture?.AssetData != null)
{
Texture2D img;
img = T2D.LoadT2DWithoutMipMaps(assetTexture.AssetData);
Texture2D img
= new Texture2D(1, 1, TextureFormat.ARGB32, false);
T2D.LoadT2DWithoutMipMaps(assetTexture.AssetData, img);
detailTexture[i] = (Texture2D)img;
}
textureDone.Set();
@@ -30,10 +30,10 @@ namespace Raindrop.Services.Bootstrap
private void Start()
{
//1. mapfetcher - logic, not ui. please refactor
if (!ServiceLocator.ServiceLocator.Instance.IsRegistered<MapFetcher>())
if (!ServiceLocator.ServiceLocator.Instance.IsRegistered<MapService>())
{
Debug.Log("UIBootstrapper creating and registering MapBackend.MapFetcher!");
ServiceLocator.ServiceLocator.Instance.Register<MapFetcher>(new MapFetcher());
ServiceLocator.ServiceLocator.Instance.Register<MapService>(new MapService());
//return;
}
+1 -1
View File
@@ -22,7 +22,7 @@ namespace Raindrop.UI.Model
class MapModel
{
// i fetch images.
private MapFetcher mapFetcher;
private MapService _mapService;
// i cache images to disk.
private MapCache mapCache;
@@ -0,0 +1,86 @@
using OpenMetaverse;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Raindrop.ServiceLocator;
using UnityEngine;
namespace Raindrop.Map.Model
{
/// <summary>
/// Provides the map textures on demand.
///
/// previously...:
/// Contains logic to fetch and decode the map textures.
/// </summary>
public class MapService : IGameService
{
public static uint regionSize = 256;
// fixme: unused
public class MapData
{
ulong gridLoc;
byte[] dataJPG;
public MapData(ulong gridLoc, byte[] dataJPG)
{
this.gridLoc = gridLoc;
this.dataJPG = dataJPG;
}
}
//a queue of map data that want to be decoded.
// fixme: unused
ConcurrentQueue<MapData> receivedDataQueue = new ConcurrentQueue<MapData>();
// fixme: unused
UnityMainThreadDispatcher mainThreadInstance;
private object mapDataQueue_lock = new object();
private MapTilesRAM _mapTilesRam;
private MapTilesNetwork mapTilesNetwork;
public MapService()
{
mainThreadInstance = UnityMainThreadDispatcher.Instance();
//the cache.
_mapTilesRam = new MapTilesRAM(10, receivedDataQueue);
// the disk.
// mapTilesDisk = new MapTilesDisk();
// the network.
mapTilesNetwork = new MapTilesNetwork(2);
}
/// <summary>
/// API to Get the map tile at specific region handle and zoom level.
/// - you will need to poll the ready flag in the Maptile until it is ready.
/// Caveats:
/// - Only zoom level 1 is supported.
/// - if not present, a webrequest for the tile will be made.
/// </summary>
/// <param name="handle"></param>
/// <param name="zoom"></param>
/// <param name="isReady">if the returned MapTile is ready for display.</param>
/// <returns></returns>
public MapTile GetMapTile(ulong handle, int zoom, out bool isReady)
{
MapTile res = null;
bool isTileAvailable = _mapTilesRam.tryGetTile_RAM(handle, out res);
if (isTileAvailable)
{
isReady = true;
return res;
}
//this call to network, returns a maptile that is empty inside for now.
res = mapTilesNetwork.GetRegionTileExternal(handle, 1);
isReady = false;
return res;
}
}
}
@@ -1,81 +1,38 @@
using OpenMetaverse;
using Raindrop.Network;
using Raindrop.UI;
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Raindrop.ServiceLocator;
using UniRx;
using OpenMetaverse;
using UnityEngine;
namespace Raindrop.Map.Model
{
/// <summary>
/// Contains logic to fetch and decode the map textures.
/// </summary>
public class MapFetcher : IGameService
// for you to get map tiles from the network.
public class MapTilesNetwork
{
public class MapData
{
ulong gridLoc;
byte[] dataJPG;
public MapData(ulong gridLoc, byte[] dataJPG)
{
gridLoc = this.gridLoc;
dataJPG = this.dataJPG;
}
}
//MapTilesManager mapTilesManager;
List<ulong> tileRequests = new List<ulong>(); // a list of pending fetch requests. the ulongs are the x and y world coordinates (gridX * 256), packed.
uint regionSize = 256;
private ParallelDownloader downloader;
int poolSize = 10;
Queue<MapData> gottenData;
private DownloadManager downloader;
UnityMainThreadDispatcher mainThreadInstance;
private object mapDataQueue;
public MapFetcher()
public MapTilesNetwork(int parallelDownloads)
{
//mapTilesManager = new MapTilesManager(10);
downloader = new ParallelDownloader();
downloader = new DownloadManager();
downloader.ParallelDownloads = parallelDownloads;
mainThreadInstance = UnityMainThreadDispatcher.Instance();
}
/// <summary>
/// API to Get the map tile at specific region handle and zoom level. Only zoom level 1 is supported.
/// gets the map tile at handle if present.
/// if not present, a webrequest for the tile will be made. a notification of download success will not be reported to the caller of this function.
/// </summary>
/// <param name="handle"></param>
/// <param name="zoom"></param>
/// <returns></returns>
public MapTile tryGetMapTile(ulong handle, int zoom)
{
MapTile res = null;
//res = mapTilesManager.tryGetTile(handle);
return res;
}
//get region tile using SL map API -- JPEG images.
// obtains the image from URL, decodes it in main thread, the stores data in the appropriate maptile.
public MapTile GetRegionTileExternal(ulong handle, int zoom)
{
zoom = 1;
if (tryGetMapTile(handle, 1) != null)
{
return tryGetMapTile(handle, 1);
}
else
{
// if (tryGetMapTile(handle, 1) != null)
// {
// //since tile is ready in the memory, return it.
// return tryGetMapTile(handle, 1);
// }
// else
// {
lock (tileRequests)
{
if (tileRequests.Contains(handle)) return null;
@@ -84,11 +41,11 @@ namespace Raindrop.Map.Model
uint regX, regY;
OpenMetaverse.Utils.LongToUInts(handle, out regX, out regY);
regX /= regionSize;
regY /= regionSize;
regX /= MapService.regionSize;
regY /= MapService.regionSize;
//int zoom = 1;
MapTile res = new MapTile(256,256);
downloader.QueueDownlad(
downloader.QueueDownload(new DownloadRequest(
new Uri(string.Format("http://map.secondlife.com/map-{0}-{1}-{2}-objects.jpg", zoom, regX, regY)),
20 * 1000,
null,
@@ -100,31 +57,29 @@ namespace Raindrop.Map.Model
try
{
//1. download is ready now.
//2. decode the image.
//3. add it to the hashtable in the model (memory)
//3. add it to the hashtable in the model (memory)
//2. decode the image on the main thread.
//4. exit.
Debug.Log("fetching the texture was successful!");
//lock (mapData.sceneTiles)
//{
//decode http response data into texture2d
//MapTile tex = mapData.getTile();
lock (mapDataQueue)
{
gottenData.Enqueue(new MapData(handle, responseData));
}
// removed. reason: initially we wanted to push into a queue.
// now realise want to simplify. just decode on main thread,
// let the view keep polling the maptile object for ready flag.
// lock (mapDataQueue_lock)
// {
// receivedDataQueue.Enqueue(new MapService.MapData(handle, responseData));
// }
//run jpeg decoding on the main thread, for now.
//start to run jpeg decoding on the main thread.
mainThreadInstance.Enqueue(DecodeDataToTexAsync(res, responseData));
// remove from request list
lock (tileRequests)
{
if (tileRequests.Contains(handle))
tileRequests.Remove(handle);
}
//res = tile;
}
catch (Exception e)
{
@@ -132,28 +87,32 @@ namespace Raindrop.Map.Model
}
}
}
)
);
return res; // inside res, it is notready.
return res; // the tile is created. but inside the tile, it is not ready and the texture is the empty texture static
//lock (mapData.sceneTiles)
//{
// mapData.sceneTiles[handle] = mapData.acquireTile(); //wtf!
//}
}
// }
}
private IEnumerator DecodeDataToTexAsync(MapTile output_tile, byte[] responseData)
// decode jpg bytes to texture2d.
//must be run on the main thread as we are manipulating T2D
// 1. decode JPEGbytes to texture2d
// 2. replace the corresponding tiles's T2D
private IEnumerator DecodeDataToTexAsync(MapTile tile, byte[] responseData)
{
try
{
lock (output_tile.getTex())
lock (tile) //lock the tile that you want to write into.
{
Texture2D _tex = output_tile.getTex();
bool success = _tex.LoadImage(responseData); //warn: main thread only.
output_tile.isReady = true;
Texture2D _tex = tile.getTex();
bool success = _tex.LoadImage(responseData); //warn: main thread only. fixme.
tile.isReady = true;
}
}
@@ -165,5 +124,7 @@ namespace Raindrop.Map.Model
yield return null;
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 551a208562ef49b1bf29bfac4b99f040
timeCreated: 1642786871
+2 -2
View File
@@ -8,7 +8,7 @@ namespace Raindrop.Map.Model
{
private Queue<MapTile> tilePool;
public int count => tilePool.Count;
public int Count => tilePool.Count;
public MapTilesPool(int poolSize)
@@ -23,7 +23,7 @@ namespace Raindrop.Map.Model
// gets a unused maptile from pool.
public MapTile acquireTile()
{
if (count <= 0)
if (Count <= 0)
{
throw new Exception("ran out of memory in mapdatapool! Please implement data recycling logic.");
}
@@ -1,5 +1,6 @@
using OpenMetaverse;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Raindrop.Map.Model
@@ -7,16 +8,19 @@ namespace Raindrop.Map.Model
/// <summary>
/// API to retrieve, delete, modify, create MapTiles in the scene
/// </summary>
public class MapTilesManager
public class MapTilesRAM
{
//number of tiles that are in the scene.
public int visibleCount => sceneTiles.Count;
int poolSize => pool.Count;
//tiles that are in the scene.
private Dictionary<ulong, MapTile> sceneTiles = new Dictionary<ulong, MapTile>();
private MapTilesPool pool;
public MapTilesManager(int poolSize)
private ConcurrentQueue<MapService.MapData> readyForDecode_DataQueue;
public MapTilesRAM(int poolSize, ConcurrentQueue<MapService.MapData> receivedDataQueue)
{
pool = new MapTilesPool(poolSize);
}
@@ -36,11 +40,11 @@ namespace Raindrop.Map.Model
/// </summary>
/// <param name="handle">the region handle of the tile to get ; gridCoords * 256 and pack X&Y together.</param>
/// <returns> Tile </returns>
public MapTile tryGetTile(ulong handle)
public bool tryGetTile_RAM(ulong handle, out MapTile tile)
{
MapTile tile = null;
// MapTile tile = null;
bool success = sceneTiles.TryGetValue(handle, out tile);
return tile;
return success;
}
/// <summary>
@@ -1,8 +0,0 @@
namespace Raindrop
{
public interface SpawnableObject
{
void Spawn();
void DeSpawn();
}
}
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: af6f4e5269f7aba4fa7a0d038cca70e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -20,7 +20,7 @@ public class MapViewer_SingleStatic_NoZoom : MonoBehaviour
{
[SerializeField]
public MapFetcher mapFetcher;
public MapService MapService;
[SerializeField]
public GameObject mapTileGO;
@@ -43,7 +43,7 @@ public class MapViewer_SingleStatic_NoZoom : MonoBehaviour
System.Threading.Timer repaint;
private void Awake()
{
mapFetcher = new MapFetcher();
MapService = new MapService();
}
@@ -78,7 +78,7 @@ public class MapViewer_SingleStatic_NoZoom : MonoBehaviour
throw new System.Exception("mapMoverGO is fucked"); // fix exception type plz
}
mapFetcher = new MapFetcher();
MapService = new MapService();
// for testing
@@ -93,11 +93,13 @@ public class MapViewer_SingleStatic_NoZoom : MonoBehaviour
ulong handle = mapMover.GetLookAt();
int zoom = (int)zoomSlider.value;
// how 2 convert look at floats into uints? (uints are gridpos * 256)
if (mapFetcher.tryGetMapTile(handle, 1) == null) //hack for now.
{
mapFetcher.GetRegionTileExternal(handle, 1);
}
MapTile mt = MapService.GetMapTile(handle, 1, out bool isReady);
//
// if (MapService.GetMapTile(handle, 1) == null) //hack for now.
// {
// MapService.GetMapTile(handle, 1, out bool isReady);
// }
//
needRepaint = true;
Debug.Log("refreshed internal images. fetching images if any..");
@@ -119,12 +121,13 @@ public class MapViewer_SingleStatic_NoZoom : MonoBehaviour
return;
}
ulong handle = mapMover.GetLookAt();
MapTile tex = mapFetcher.tryGetMapTile(handle, 1);
if (tex != null)
{
iv.setRawImage(tex.getTex());
}
Debug.Log("drew images.");
// MapTile tex = MapService.GetMapTile(handle, 1);
throw new NotImplementedException("we recently refactored the mapservice interface.");
// if (tex != null)
// {
// iv.setRawImage(tex.getTex());
// }
// Debug.Log("drew images.");
}
}
@@ -17,13 +17,13 @@ namespace Raindrop
class TestTextureFetcher : MonoBehaviour
{
[SerializeField]
public MapFetcher mapFetcher; //fetching logic + pooling data here
public MapService MapService; //fetching logic + pooling data here
private void Awake()
{
MainThreadDispatcher.Initialize();
mapFetcher = new MapFetcher();
MapService = new MapService();
}
@@ -36,7 +36,8 @@ namespace Raindrop
var handle = Utils.UIntsToLong(256 * 1000, 256 * 1000);
mapFetcher.GetRegionTileExternal(handle, 1);
throw new NotImplementedException("we recently refactored the mapservice interface.");
// MapService.GetRegionTileExternal(handle, 1);
Debug.Log("end of initial fetch call.");
}