/// <summary> /// Load an asset to the preview. /// </summary> /// <typeparam name="TAssetType">The type of the asset to load</typeparam> /// <param name="url">The path to the asset to load</param> /// <param name="settings">The settings. If null, fallback to <see cref="ContentManagerLoaderSettings.Default" />.</param> /// <returns>The loaded asset</returns> public TAssetType LoadAsset <TAssetType>(string url, ContentManagerLoaderSettings settings = null) where TAssetType : class { TAssetType result = null; try { // This method can be invoked both from a script and from a regular task. In the second case, it will use the out-of-microthread database which need to be locked. // TODO: Ensure this method is always called from the preview game (it is not at least when a property is modified, currently), so we don't need to lock. Note: should be the case now, assume it is after GDC! if (Scheduler.CurrentMicroThread == null) { Monitor.Enter(AssetBuilderService.OutOfMicrothreadDatabaseLock); } MicrothreadLocalDatabases.MountDatabase(OutputObjects.Yield()); try { result = Game.Content.Load <TAssetType>(url, settings); } finally { if (Scheduler.CurrentMicroThread == null) { MicrothreadLocalDatabases.UnmountDatabase(); } } } catch (Exception e) { Builder.Logger.Error($"An exception was triggered when trying to load the entity [{url}] for the preview of asset item [{AssetItem.Location}].", e); } finally { if (Scheduler.CurrentMicroThread == null) { Monitor.Exit(AssetBuilderService.OutOfMicrothreadDatabaseLock); } } return(result); }
public override void PostCommand(ICommandContext commandContext, ResultStatus status) { base.PostCommand(commandContext, status); if (status == ResultStatus.Successful) { // Save list of newly changed URLs in CommandResult.OutputObjects foreach (var entry in buildTransaction.GetTransactionIdMap()) { commandContext.RegisterOutput(entry.Key, entry.Value); } // Note: In case of remote process, the remote process will save the index map. // Alternative would be to not save it and just forward results to the master builder who would commit results locally. // Not sure which is the best. // // Anyway, current approach should be OK for now since the index map is "process-safe" (as long as we load new values as necessary). //contentIndexMap.Save(); } MicrothreadLocalDatabases.UnmountDatabase(); }
/// <summary> /// The micro-thread in charge of processing the thumbnail build requests and creating the thumbnails. /// </summary> private ResultStatus ProcessThumbnailRequests(ThumbnailBuildRequest request) { var status = ResultStatus.Successful; // Global lock so that only one rendering happens at the same time lock (lockObject) { try { lock (AssetBuilderService.OutOfMicrothreadDatabaseLock) { MicrothreadLocalDatabases.MountCommonDatabase(); // set the master output var renderTarget = GraphicsContext.Allocator.GetTemporaryTexture2D(request.Size.X, request.Size.Y, request.ColorSpace == ColorSpace.Linear ? PixelFormat.R8G8B8A8_UNorm_SRgb : PixelFormat.R8G8B8A8_UNorm, TextureFlags.ShaderResource | TextureFlags.RenderTarget); var depthStencil = GraphicsContext.Allocator.GetTemporaryTexture2D(request.Size.X, request.Size.Y, PixelFormat.D24_UNorm_S8_UInt, TextureFlags.DepthStencil); try { // Fake presenter // TODO GRAPHICS REFACTOR: Try to remove that GraphicsDevice.Presenter = new RenderTargetGraphicsPresenter(GraphicsDevice, renderTarget, depthStencil.ViewFormat); // Always clear the state of the GraphicsDevice to make sure a scene doesn't start with a wrong setup GraphicsCommandList.ClearState(); // Setup the color space when rendering a thumbnail GraphicsDevice.ColorSpace = request.ColorSpace; // render the thumbnail thumbnailScene.Children.Add(request.Scene); // Store the graphics compositor to use, so we can dispose it when disposing this ThumbnailGenerator thumbnailGraphicsCompositors.Add(request.GraphicsCompositor); sceneSystem.GraphicsCompositor = request.GraphicsCompositor; // Render once to setup render processors // TODO GRAPHICS REFACTOR: Should not require two rendering GraphicsContext.ResourceGroupAllocator.Reset(GraphicsContext.CommandList); gameSystems.Draw(nullGameTime); // Draw gameSystems.Update(nullGameTime); GraphicsContext.ResourceGroupAllocator.Reset(GraphicsContext.CommandList); gameSystems.Draw(nullGameTime); // write the thumbnail to the file using (var thumbnailImage = renderTarget.GetDataAsImage(GraphicsCommandList)) using (var outputImageStream = request.FileProvider.OpenStream(request.Url, VirtualFileMode.Create, VirtualFileAccess.Write)) { request.PostProcessThumbnail?.Invoke(thumbnailImage); ThumbnailBuildHelper.ApplyThumbnailStatus(thumbnailImage, request.DependencyBuildStatus); thumbnailImage.Save(outputImageStream, ImageFileType.Png); request.Logger.Info($"Thumbnail creation successful [{request.Url}] to ({thumbnailImage.Description.Width}x{thumbnailImage.Description.Height},{thumbnailImage.Description.Format})"); } } finally { // Cleanup the scene thumbnailScene.Children.Clear(); sceneSystem.GraphicsCompositor = null; GraphicsContext.Allocator.ReleaseReference(depthStencil); GraphicsContext.Allocator.ReleaseReference(renderTarget); } MicrothreadLocalDatabases.UnmountDatabase(); } } catch (Exception e) { status = ResultStatus.Failed; request.Logger.Error("An exception occurred while processing thumbnail request.", e); } } return(status); }