// Invoked when a load requeset has failed to download the material data it needs // Requests are retried RETRY_LIMIT times if they fail more than that the request is abandoned (error is logged) // When this happens if any dependent cubes want the resource that failed one request is re-queued to try again (under that request's Retry quota) private void FailGetMaterialDataRequest(LoadCubeRequest loadRequest, string materialPath) { CheckIfBackgroundThread(); loadRequest.Failures++; lock (MaterialDataCache) { // Remove the 'in progress' marker from the cache MaterialDataCache.Remove(materialPath); } if (RETRY_LIMIT > loadRequest.Failures) { Debug.LogError("Retry limit hit for: " + materialPath); Debug.LogError("Cube load failed for " + loadRequest); lock (_dependentCubes) { LinkedList <LoadCubeRequest> dependentRequests; if (_dependentCubes.TryGetValue(materialPath, out dependentRequests)) { var request = dependentRequests.Last.Value; dependentRequests.RemoveLast(); _loadMaterialQueue.ConcurrentEnqueue(request).Wait(); } } } else { // Queue for retry _loadMaterialQueue.ConcurrentEnqueue(loadRequest).Wait(); } }
// Helper for locking a queue, pulling off requests and invoking a handler function for them private void ProcessQueue(Queue <LoadCubeRequest> queue, Func <LoadCubeRequest, IEnumerator> requestProcessFunc, int limit) { var noLimit = limit == 0; if (Monitor.TryEnter(queue)) { try { while (queue.Count > 0 && (noLimit || (limit-- > 0))) { var request = queue.Dequeue(); if (!request.Cancelled) { StartCoroutine(requestProcessFunc(request)); } else { lock (MaterialDataCache) { if (request.MaterialData != null && request.MaterialData.DiffuseTex != null) { MaterialDataCache.Release(request.MaterialData.DiffuseTex.name); } } CancelledRequests++; } } } finally { Monitor.Exit(queue); } } }
void OnDestroy() { if (MaterialDataCache != null) { MaterialDataCache.Empty(); } if (_geometryBufferCache != null) { _geometryBufferCache.Empty(); } }
// Used to create material data when a texture has finished downloading private IEnumerator FinishCreatingMaterialDataWithTexture( KeyValuePair <string, byte[]> materialDataKeyAndTexturePair) { var materialDataKey = materialDataKeyAndTexturePair.Key; while (!Monitor.TryEnter(_partiallyConstructedMaterialDatas)) { yield return(null); } MaterialData inProgressMaterialData; try { inProgressMaterialData = _partiallyConstructedMaterialDatas[materialDataKey]; _partiallyConstructedMaterialDatas.Remove(materialDataKey); } finally { Monitor.Exit(_partiallyConstructedMaterialDatas); } if (!CacheFill) { #if UNITY_IOS var texture = new Texture2D(1, 1, TextureFormat.RGB24, false); #else var texture = new Texture2D(1, 1, TextureFormat.DXT1, false); #endif texture.LoadImage(materialDataKeyAndTexturePair.Value); inProgressMaterialData.DiffuseTex = texture; inProgressMaterialData.DiffuseTex.name = inProgressMaterialData.Name; } while (!Monitor.TryEnter(MaterialDataCache)) { yield return(null); } try { MaterialDataCache[materialDataKey] = inProgressMaterialData; // Add reference until we add references for dependent requests MaterialDataCache.AddRef(materialDataKey); } finally { Monitor.Exit(MaterialDataCache); } // Move forward dependent requests that wanted this material data yield return(StartCoroutine(SucceedGetMaterialDataRequests(materialDataKey, inProgressMaterialData))); }
// Called when the material data has been constructed into the cache // The material data is constructed using a materialkey for reference // The method sets the material data for any dependent requests and moves them along private IEnumerator SucceedGetMaterialDataRequests(string materialDataKey, MaterialData materialData) { CheckIfMainThread(); // Check to see if any other requests were waiting on this model LinkedList <LoadCubeRequest> dependentRequests; while (!Monitor.TryEnter(_dependentCubes)) { yield return(null); } try { if (_dependentCubes.TryGetValue(materialDataKey, out dependentRequests)) { _dependentCubes.Remove(materialDataKey); } } finally { Monitor.Exit(_dependentCubes); } while (!Monitor.TryEnter(MaterialDataCache)) { yield return(null); } try { // If any were send them to their next stage if (dependentRequests != null) { foreach (var request in dependentRequests) { request.MaterialData = materialData; MaterialDataCache.AddRef(request.MaterialData.DiffuseTexPath); MoveRequestForward(request); } } // Now that added references for the dependent requests. We can release the interim reference MaterialDataCache.Release(materialData.DiffuseTexPath); } finally { Monitor.Exit(MaterialDataCache); } }
private void InternalSetup() { #if !UNITY_WSA _mainThread = Thread.CurrentThread; #endif _guiStyle.normal.textColor = Color.red; if (_geometryBufferCache == null) { _geometryBufferCache = new DictionaryCache <string, GeometryBuffer>(GeometryBufferCacheSize); } else { Debug.LogWarning("GeometryBuffer cache already initialized. Skipping initizliation."); } if (MaterialDataCache == null) { MaterialDataCache = new MaterialDataCache(MaterialDataCacheSize); } else { Debug.LogWarning("Material Data cache already initialized. Skipping initizliation."); } ObjectPooler.Current.CreatePoolForObject(BaseModelCube); // Optional pool only used in camera detection scenario if (PlaceHolderCube != null) { ObjectPooler.Current.CreatePoolForObject(PlaceHolderCube); } CacheWebRequest.InitializeCache(CacheSize, ProxyUrl); }
// Responsible for getting the material data for a load request // The method works roughly as follows // 1. Check if material data is in cache // a. If not, start a web request for the data and add this request to the dependency list for the path // 2. If the material data cache indicates that it is being filled (a set value of null) (including if the // request just started during this invocation) add the request to the dependency list for this path // 3. If the material is in the cache and set then get the data for the request and move it forward private IEnumerator GetMaterialForRequest(LoadCubeRequest loadRequest) { var pyriteLevel = loadRequest.Query.DetailLevels[loadRequest.LodIndex]; var textureCoordinates = pyriteLevel.TextureCoordinatesForCube(loadRequest.X, loadRequest.Y); var texturePath = loadRequest.Query.GetTexturePath(loadRequest.LodIndex, (int)textureCoordinates.x, (int)textureCoordinates.y); while (!Monitor.TryEnter(MaterialDataCache)) { yield return(null); } // If the material data is not in the cache or in the middle of being constructed add this request as a dependency if (!MaterialDataCache.ContainsKey(texturePath) || MaterialDataCache[texturePath] == null) { // Add this requst to list of requests that is waiting for the data yield return(StartCoroutine(AddDependentRequest(loadRequest, texturePath))); // Check if this is the first request for material (or it isn't in the cache) if (!MaterialDataCache.ContainsKey(texturePath)) { if (UseWwwForTextures) { // Material data was not in cache nor being constructed // Cache counter MaterialCacheMisses++; // Set to null to signal to other tasks that the key is in the process // of being filled MaterialDataCache[texturePath] = null; var materialData = CubeBuilderHelpers.GetDefaultMaterialData((int)textureCoordinates.x, (int)textureCoordinates.y, loadRequest.LodIndex, texturePath); var cachePath = CacheWebRequest.GetCacheFilePath(texturePath); if (!CacheFill) { WWW textureWww; // = new WWW(texturePath); if (CacheWebRequest.IsItemInCache(cachePath)) { FileCacheHits++; textureWww = new WWW("file:///" + cachePath); yield return(textureWww); } else { FileCacheMisses++; textureWww = new WWW(texturePath); yield return(textureWww); if (textureWww.Succeeded()) { CacheWebRequest.AddToCache(cachePath, textureWww.bytes); } } if (textureWww.Failed()) { Debug.LogError("Error getting texture [" + texturePath + "] " + textureWww.error); FailGetMaterialDataRequest(loadRequest, texturePath); } else { materialData.DiffuseTex = textureWww.textureNonReadable; materialData.DiffuseTex.name = materialData.Name; } } MaterialDataCache[texturePath] = materialData; // Add a reference to keep the texture around until we queue off the related load requests MaterialDataCache.AddRef(texturePath); // Move forward dependent requests that wanted this material data yield return(StartCoroutine(SucceedGetMaterialDataRequests(texturePath, materialData))); } else { // Material data was not in cache nor being constructed // Cache counter MaterialCacheMisses++; // Set to null to signal to other tasks that the key is in the process // of being filled MaterialDataCache[texturePath] = null; var materialData = CubeBuilderHelpers.GetDefaultMaterialData((int)textureCoordinates.x, (int)textureCoordinates.y, loadRequest.LodIndex, texturePath); _partiallyConstructedMaterialDatas[texturePath] = materialData; CacheWebRequest.GetBytes(texturePath, textureResponse => { CheckIfBackgroundThread(); if (textureResponse.Status == CacheWebRequest.CacheWebResponseStatus.Error) { Debug.LogError("Error getting texture [" + texturePath + "] " + textureResponse.ErrorMessage); FailGetMaterialDataRequest(loadRequest, texturePath); } else if (textureResponse.Status == CacheWebRequest.CacheWebResponseStatus.Cancelled) { lock (MaterialDataCache) { MaterialDataCache.Remove(texturePath); } } else { if (textureResponse.IsCacheHit) { FileCacheHits++; } else { FileCacheMisses++; } _texturesReadyForMaterialDataConstruction.ConcurrentEnqueue( new KeyValuePair <string, byte[]>(texturePath, textureResponse.Content)).Wait(); } }, DependentRequestsExistBlocking); } } } else // The material was in the cache { // Material data ready get it and move on MaterialCacheHits++; loadRequest.MaterialData = MaterialDataCache[texturePath]; MaterialDataCache.AddRef(texturePath); MoveRequestForward(loadRequest); } Monitor.Exit(MaterialDataCache); }