public void Update() { if (m_future != null) { try { string[] results; if (m_future.TryGetResult(out results)) { Succeeded = m_successTest == null ? false : m_successTest(results); Output = results; FinishTime = DateTime.Now; m_future = null; HasRun = true; if (Completed != null) { Completed(Succeeded); Completed = null; } } } catch (FutureFailed ex) { Output = ex.Message.Split('\n'); FinishTime = DateTime.Now; m_future = null; Succeeded = false; HasRun = true; } } }
private void ScanAndroidDevices() { if (m_deviceListFuture == null) { if ((DateTime.Now - m_timeOfLastDeviceScan).TotalSeconds > kSecondsBetweenDeviceScan && !EditorApplication.isPlaying) { m_deviceListFuture = new Future <string[]>(() => RunAdb("devices")); } } else { string[] results; if (m_deviceListFuture.TryGetResult(out results)) { m_androidDevices = results.Skip(1).Where(x => !string.IsNullOrEmpty(x.Trim())) .Select(x => x.Split(' ', '\t')[0]).ToArray(); if (m_androidDevices.Length != 0 && !m_androidDevices.Contains(m_selectedAndroid)) { m_selectedAndroid = m_androidDevices[0]; } m_deviceListFuture = null; Repaint(); m_timeOfLastDeviceScan = DateTime.Now; } } }
/// Waits for the json data to be read on a background thread, and then executes a precache /// coroutine for each found asset. private IEnumerator<Null> PrecacheModelsCoroutine(SceneFileInfo sceneFileInfo, string reason) { var getIdsFuture = new Future<List<string>>(() => GetModelIds(sceneFileInfo)); List<string> ids; while (true) { try { if (getIdsFuture.TryGetResult(out ids)) { break; } } catch (FutureFailed e) { throw new Exception($"While reading {sceneFileInfo}", e); } yield return null; } if (ids == null) { yield break; } List<IEnumerator<Null>> precacheCoroutines = new List<IEnumerator<Null>>(); // Only trigger off one precache routine per frame. foreach (string id in ids) { if (m_ModelsByAssetId.ContainsKey(id)) { // Already cached continue; } if (! FileUtils.InitializeDirectory(GetCacheDirectoryForAsset(id))) { continue; } precacheCoroutines.Add(PrecacheCoroutine( VrAssetService.m_Instance.GetAsset(id, VrAssetFormat.GLTF2, reason))); yield return null; } var cr = CoroutineUtil.CompleteAllCoroutines(precacheCoroutines); while (cr.MoveNext()) { yield return cr.Current; } }
static T Force <T>(Future <T> f) where T : class { T value; while (!f.TryGetResult(out value)) { } return(value); }
// Helper for State.Looking // Updates: // - m_CapturesFuture // - m_SelectNextCaptureTimer // - m_AudioCapture // On failure, m_AudioCapture is unchanged (still null). void SelectNextCapture() { // So we don't have to worry about calling Dispose() on these things Debug.Assert(m_AudioCapture == null); Debug.Assert(m_FinalSource == null); Queue <WasapiCapture> captures; if (m_CapturesFuture == null) { m_CapturesFuture = new Future <Queue <WasapiCapture> >(GetAudioCaptures, CleanupAudioCaptures); m_nCapturesFound = -1; m_SearchFoundNoAudio = false; } if (!m_CapturesFuture.TryGetResult(out captures)) { // Future not ready yet. Retry again next frame; it should complete quickly. m_SelectNextCaptureTimer = 0; return; } // The count decrements as we consume, but m_nCapturesFound will be the high water mark m_nCapturesFound = Mathf.Max(m_nCapturesFound, captures.Count); if (m_nCapturesFound == 0) { // Future is ready, but it says there are no sources ControllerConsoleScript.m_Instance.AddNewLine("No audio sources available."); m_SearchFoundNoAudio = true; StopFuture(); // Re-query for captures after timeout m_SelectNextCaptureTimer = m_SelectDeviceTimeoutDuration; return; } if (captures.Count == 0) { // Future is ready, and there were sources, but we've tried them all StopFuture(); // Re-query for captures next frame m_SelectNextCaptureTimer = 0; return; } var capture = captures.Dequeue(); string line = string.Format( "Trying {1}/{2}: {0}", capture.Device.FriendlyName, m_nCapturesFound - captures.Count, m_nCapturesFound); ControllerConsoleScript.m_Instance.AddNewLine(line); m_AudioCapture = capture; m_SampleRate = m_AudioCapture.WaveFormat.SampleRate; m_FriendlyName = m_AudioCapture.Device.FriendlyName; m_SelectNextCaptureTimer = m_SelectDeviceTimeoutDuration; }
private IEnumerable RequestLoadIconAndMetadataCoroutineThreaded() { var thumbFuture = new Future <byte[]>(() => ReadThumbnail(m_FileInfo)); byte[] data; while (!thumbFuture.TryGetResult(out data)) { yield return(null); } if (data != null && data.Length > 0) { if (m_Icon == null) { m_Icon = new Texture2D(128, 128, TextureFormat.RGB24, true); } m_Icon.LoadImage(data); m_Icon.Apply(); } else { // TODO: why is the icon missing/invalid? should we be noisier // about invalid files? But RequestLoadIcon() doesn't have a // way of indicating "not a tilt" } if (m_Authors == null) { var metadataFuture = new Future <SketchMetadata>(() => m_FileInfo.ReadMetadata()); SketchMetadata metadata; while (!metadataFuture.TryGetResult(out metadata)) { yield return(null); } if (metadata != null) { m_Authors = metadata.Authors; } else { if (SaveLoadScript.m_Instance.LastMetadataError != null) { ControllerConsoleScript.m_Instance.AddNewLine( string.Format("Error detected in sketch '{0}'.\nTry re-saving.", m_FileInfo.HumanName)); Debug.LogWarning(string.Format("Error reading metadata for {0}.\n{1}", m_FileInfo.FullPath, SaveLoadScript.m_Instance.LastMetadataError)); } } } m_bMetadataValid = true; }
public IEnumerator <object> NextPage(List <PolySceneFileInfo> files) { string uri = m_PageToken == null ? m_Uri : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) { try { cr.MoveNext(); } catch (VrAssetServiceException e) { e.UserFriendly = m_ErrorMessage; throw; } yield return(cr.Current); } } Future <JObject> f = new Future <JObject>(() => JObject.Parse(request.Result)); JObject json; while (!f.TryGetResult(out json)) { yield return(null); } var assets = json["assets"]; if (assets != null) { foreach (var asset in assets) { var info = new PolySceneFileInfo(asset); info.Author = asset["displayName"].ToString(); ; files.Add(info); } } JToken jPageToken = json["nextPageToken"]; m_PageToken = jPageToken != null?jPageToken.ToString() : null; }
// Delete any files that aren't referenced in m_Sketches (actually m_AssetIds) // Does this on a background thread so prevent hitches. private IEnumerator DeleteOldSketchesCoroutine() { if (m_CacheDir != null) { var task = new Future <bool>(() => { var unknown = new DirectoryInfo(m_CacheDir).GetFiles().Where( f => !m_AssetIds.ContainsKey(Path.GetFileNameWithoutExtension(f.Name))); foreach (var f in unknown) { f.Delete(); } return(true); }); bool unused; while (!task.TryGetResult(out unused)) { yield return(null); } } }
// Initiates the contact with Poly. public IEnumerator <Null> GetAssetCoroutine() { if (string.IsNullOrEmpty(App.Config.GoogleSecrets?.ApiKey)) { IsCanceled = true; yield break; } m_Ready = false; WebRequest request = new WebRequest(m_URI, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) { try { cr.MoveNext(); } catch (VrAssetServiceException e) { Debug.LogException(e); IsCanceled = true; yield break; } yield return(cr.Current); } } // Deserialize request string in to an Asset class. MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(request.Result)); using (var jsonReader = new JsonTextReader(new StreamReader(memStream))) { Future <JObject> f = new Future <JObject>(() => JObject.Parse(request.Result)); JObject json; while (!f.TryGetResult(out json)) { yield return(null); } if (json.Count == 0) { Debug.LogErrorFormat("Failed to deserialize response for {0}", m_URI); yield break; } // Find the asset by looking through the format list for the specified type. VrAssetFormat requestedType = m_Asset.DesiredType; while (true) { var format = json["formats"]?.FirstOrDefault(x => x["formatType"]?.ToString() == requestedType.ToString()); if (format != null) { string internalRootFilePath = format["root"]?["relativePath"].ToString(); // If we successfully get a gltf2 format file, internally change the extension to // "gltf2" so that the cache knows that it is a gltf2 file. if (requestedType == VrAssetFormat.GLTF2) { internalRootFilePath = Path.ChangeExtension(internalRootFilePath, "gltf2"); } // Get root element info. m_Asset.SetRootElement( internalRootFilePath, format["root"]?["url"].ToString()); // Get all resource infos. There may be zero. foreach (var r in format["resources"]) { string path = r["relativePath"].ToString(); m_Asset.AddResourceElement(path, r["url"].ToString()); // The root element should be the only gltf file. Debug.Assert(!path.EndsWith(".gltf") && !path.EndsWith(".gltf2"), string.Format("Found extra gltf resource: {0}", path)); } break; } else { // We asked for an asset in a format that it doesn't have. // In some cases, we should look for a different format as backup. if (requestedType == VrAssetFormat.GLTF2) { requestedType = VrAssetFormat.GLTF; } else { // In other cases, we should fail and get out. Debug.LogErrorFormat("Can't download {0} in {1} format.", m_Asset.Id, m_Asset.DesiredType); yield break; } } } } // Download root asset. request = new WebRequest(m_Asset.RootDataURL, App.GoogleIdentity); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) { try { cr.MoveNext(); } catch (VrAssetServiceException e) { Debug.LogErrorFormat("Error downloading {0} at {1}\n{2}", m_Asset.Id, m_Asset.RootDataURL, e); yield break; } yield return(cr.Current); } } m_Asset.CopyBytesToRootElement(request.ResultBytes); // Download all resource assets. foreach (var e in m_Asset.ResourceElements) { request = new WebRequest(e.dataURL, App.GoogleIdentity); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) { try { cr.MoveNext(); } catch (VrAssetServiceException ex) { Debug.LogErrorFormat("Error downloading {0} at {1}\n{2}", m_Asset.Id, m_Asset.RootDataURL, ex); e.assetBytes = null; yield break; } yield return(cr.Current); } } e.assetBytes = request.ResultBytes; } m_Ready = true; }
/// Returns: /// bool - false if incomplete, true upon successful completion. /// GameObject - caller should check output GameObject to determine success. /// ImportMaterialCollector - non-null upon successful completion. /// Raises an exception on unsuccessful completion. public bool TryEndAsyncLoad(out GameObject root, out ImportMaterialCollector importMaterialCollector) { // Three things happen in this function. // 1: It waits to try and get the result of reading the model on a background thread // 2: It checks the rate limiter to make sure we don't have too many of these going on at once. // 3: It enumerates through, creating meshes for the model. These are time-limited so that // it will stop if it has taken too long in a single frame. root = null; importMaterialCollector = null; if (m_meshEnumerator == null) { IDisposable state; if (!m_stateReader.TryGetResult(out state)) { return(false); } IEnumerable <Null> enumerable; m_root = DoUnityThreadWork(state, out enumerable, out m_ImportMaterialCollector); // TODO: Possible bugs if DoUnityThreadWork ever did fail: // We assume the invariant that (root == null) == (IsValid == false) // We assume the invariant that m_ImportMaterialCollector != null // We don't dispose the GameObject or the enumerable // If the user calls TryEndAsyncLoad again we might try to call DoUnityThreadWork again if (m_root == null) { return(false); } m_meshEnumerator = enumerable.GetEnumerator(); m_root.SetActive(false); } // Yield until the limiter unblocks. // Multiple calls to TryGetResult above are harmless. if (sm_Limiter.IsBlocked()) { return(false); } // Finish constructing the actual game object. Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); long numTicks = (long)((Stopwatch.Frequency * kMeshMsPerFrame) / 1000); while (true) { if (!m_meshEnumerator.MoveNext()) { m_root.SetActive(true); root = m_root; importMaterialCollector = m_ImportMaterialCollector; stopwatch.Stop(); return(true); } if (stopwatch.ElapsedTicks > numTicks) { stopwatch.Stop(); return(false); } } }
public IEnumerator <Null> NextPage(List <PolyAssetCatalog.AssetDetails> files, string thumbnailSuffix) { string uri = m_PageToken == null ? m_Uri : String.Format("{0}&page_token={1}", m_Uri, m_PageToken); WebRequest request = new WebRequest(uri, App.GoogleIdentity, UnityWebRequest.kHttpVerbGET); using (var cr = request.SendAsync().AsIeNull()) { while (!request.Done) { try { cr.MoveNext(); } catch (VrAssetServiceException e) { e.UserFriendly = m_ErrorMessage; throw; } yield return(cr.Current); } } Future <JObject> f = new Future <JObject>(() => JObject.Parse(request.Result)); JObject json; while (!f.TryGetResult(out json)) { yield return(null); } if (json.Count == 0) { yield break; } JToken lastAsset = null; var assets = json["assets"] ?? json["userAssets"]; foreach (JToken possibleAsset in assets) { try { // User assets are nested in an 'asset' node. JToken asset = possibleAsset["asset"] ?? possibleAsset; if (asset["visibility"].ToString() == "PRIVATE") { continue; } // We now don't filter the liked Poly objects, but we don't want to return liked Tilt Brush // sketches so in this section we filter out anything with a Tilt file in it. // Also, although currently all Poly objects have a GLTF representation we should probably // not rely on that continuing, so we discard anything that doesn't have a GLTF (1) // representation. We look for PGLTF and GLTF as for a lot of objects Poly is returning // PGLTF without GLTF. bool skipObject = false; foreach (var format in asset["formats"]) { var formatType = format["formatType"].ToString(); if (formatType == "TILT") { skipObject = true; break; } } if (skipObject) { continue; } lastAsset = asset; string accountName = asset["authorName"]?.ToString() ?? "Unknown"; files.Add(new PolyAssetCatalog.AssetDetails(asset, accountName, thumbnailSuffix)); } catch (NullReferenceException) { UnityEngine.Debug.LogErrorFormat("Failed to load asset: {0}", lastAsset == null ? "NULL" : lastAsset.ToString()); } yield return(null); } JToken jPageToken = json["nextPageToken"]; m_PageToken = jPageToken != null?jPageToken.ToString() : null; }