public override void PreCommand(ICommandContext commandContext) { base.PreCommand(commandContext); buildTransaction = MicrothreadLocalDatabases.CreateTransaction(commandContext.GetOutputObjectsGroups()); MicrothreadLocalDatabases.MountDatabase(buildTransaction); }
protected override void LoadContent() { // determine the adequate blend state to render the font adequateBlendState = Asset.Type.PremultiplyAlpha ? BlendStates.AlphaBlend : BlendStates.NonPremultiplied; // Load the texture (but don't use streaming so it will be fully loaded) texture = LoadAsset <Texture>(AssetItem.Location, ContentManagerLoaderSettings.StreamingDisabled); NotifyTextureLoaded?.Invoke(); // Update the effect MicrothreadLocalDatabases.MountCommonDatabase(); currentEffect.Parameters.Set(PreviewTextureParameters.Is3D, texture.ViewDimension == TextureDimension.Texture3D); // create texture views for cube textures. if (texture.ViewDimension == TextureDimension.TextureCube) { for (int i = 0; i < texture.ArraySize; i++) { textureCubeViews[(TextureCubePreviewMode)i] = texture.ToTextureView(new TextureViewDescription { ArraySlice = i, Type = ViewType.ArrayBand }); } } textureSourceRegion = new Rectangle(0, 0, TextureWidth, TextureHeight); // TODO: Return LDR or HDR depending on texture bits (16bits is most likely HDR) RenderingMode = RenderingMode.LDR; }
protected override void PostBuild() { base.PostBuild(); if (mergeInCommonDatabase) { MicrothreadLocalDatabases.AddToSharedGroup(buildStep.OutputObjects); } }
/// <inheritdoc /> protected override void Initialize() { // Database is needed by effect compiler cache MicrothreadLocalDatabases.MountCommonDatabase(); base.Initialize(); // TODO: the physics system should not be registered by default here! Physics.Simulation.DisableSimulation = true; }
private async Task ProcessPreviewRequestsTask() { while (IsRunning) { await Script.NextFrame(); PreviewRequest request; lock (requestLock) { request = previewRequest; previewRequest = null; } if (request != null) { try { MicrothreadLocalDatabases.MountCommonDatabase(); Faulted = false; previewScene.Children.Clear(); if (SceneSystem.GraphicsCompositor != request.GraphicsCompositor) { SceneSystem.GraphicsCompositor?.Dispose(); SceneSystem.GraphicsCompositor = request.GraphicsCompositor; } if (request.Scene != null) { previewScene.Children.Add(request.Scene); } request.RequestCompletion.SetResult(ResultStatus.Successful); } catch (Exception e) { // end the thumbnail build task request.Logger.Error("An exception occurred while loading the preview scene.", e); request.RequestCompletion.SetResult(ResultStatus.Failed); } } if (previewScene.Children.Count != 0) { var handler = UpdateSceneCallback; if (handler != null) { var renderingMode = handler(); } } } }
/// <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 async Task <IDisposable> MountInCurrentMicroThread() { if (isDisposed) { throw new ObjectDisposedException(nameof(GameStudioDatabase)); } if (Scheduler.CurrentMicroThread == null) { throw new InvalidOperationException("The database can only be mounted in a micro-thread."); } var lockObject = await databaseLock.LockAsync(); // Return immediately if the database was disposed when waiting for the lock if (isDisposed) { return(lockObject); } MicrothreadLocalDatabases.MountDatabase(database.Yield()); return(lockObject); }
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(); }
public GameStudioBuilderService(SessionViewModel sessionViewModel, GameSettingsProviderService settingsProvider, string buildDirectory, bool createDebugTools = true) : base(buildDirectory) { this.createDebugTools = createDebugTools; if (createDebugTools) { assetBuilderServiceDebugPage = EditorDebugTools.CreateLogDebugPage(GlobalLogger.GetLogger("AssetBuilderService"), "AssetBuilderService"); effectCompilerServiceDebugPage = EditorDebugTools.CreateLogDebugPage(GlobalLogger.GetLogger("EffectCompilerCache"), "EffectCompilerCache"); } SessionViewModel = sessionViewModel ?? throw new ArgumentNullException(nameof(sessionViewModel)); var shaderImporter = new XenkoShaderImporter(); var shaderBuildSteps = shaderImporter.CreateSystemShaderBuildSteps(sessionViewModel); shaderBuildSteps.StepProcessed += ShaderBuildStepsStepProcessed; PushBuildUnit(new PrecompiledAssetBuildUnit(AssetBuildUnitIdentifier.Default, shaderBuildSteps, true)); Database = new GameStudioDatabase(this, settingsProvider); const string shaderBundleUrl = "/binary/editor/EditorShadersD3D11.bundle"; if (VirtualFileSystem.FileExists(shaderBundleUrl)) { Builder.ObjectDatabase.BundleBackend.LoadBundleFromUrl("EditorShadersD3D11", Builder.ObjectDatabase.ContentIndexMap, shaderBundleUrl, true).Wait(); } // Use a shared database for our shader system // TODO: Shaders compiled on main thread won't actually be visible to MicroThread build engine (contentIndexMap are separate). // It will still work and cache because EffectCompilerCache caches not only at the index map level, but also at the database level. // Later, we probably want to have a GetSharedDatabase() allowing us to mutate it (or merging our results back with IndexFileCommand.AddToSharedGroup()), // so that database created with MountDatabase also have all the newest shaders. taskScheduler = new EffectPriorityScheduler(ThreadPriority.BelowNormal, Math.Max(1, Environment.ProcessorCount / 2)); TaskSchedulerSelector taskSchedulerSelector = (mixinTree, compilerParameters) => taskScheduler.GetOrCreatePriorityGroup(compilerParameters?.TaskPriority ?? 0); effectCompiler = (EffectCompilerBase)EffectSystem.CreateEffectCompiler(MicrothreadLocalDatabases.GetSharedDatabase(), taskSchedulerSelector: taskSchedulerSelector); StartPushNotificationsTask(); }
/// <inheritdoc /> protected override async Task LoadContent() { await base.LoadContent(); // mount the common database in this micro-thread to have access to the built effects, etc. MicrothreadLocalDatabases.MountCommonDatabase(); // Create fallback effect to use when material is still loading fallbackColorMaterial = Material.New(GraphicsDevice, new MaterialDescriptor { Attributes = { Diffuse = new MaterialDiffuseMapFeature(new ComputeTextureColor()), DiffuseModel = new MaterialDiffuseLambertModelFeature() } }); fallbackTextureMaterial = Material.New(GraphicsDevice, new MaterialDescriptor { Attributes = { Diffuse = new MaterialDiffuseMapFeature(new ComputeTextureColor { FallbackValue = null }), // Do not use fallback value, we want a DiffuseMap DiffuseModel = new MaterialDiffuseLambertModelFeature() } }); // Listen to all Renderer Initialized to plug dynamic effect compilation RenderContext.GetShared(Services).RendererInitialized += SceneGameRendererInitialized; // Update the marker render target setter viewport //OnClientSizeChanged(this, EventArgs.Empty); // Initialize the services var initialized = new List <IEditorGameService>(); foreach (var service in EditorServices.OrderByDependency()) { // Check that the current service dependencies have been initialized foreach (var dependency in service.Dependencies) { if (!initialized.Any(x => dependency.IsInstanceOfType(x))) { throw new InvalidOperationException($"The service [{service.GetType().Name}] requires a service of type [{dependency.Name}] to be initialized first."); } } if (await service.InitializeService(this)) { initialized.Add(service); } var mouseService = service as EditorGameMouseServiceBase; mouseService?.RegisterMouseServices(EditorServices); } // TODO: Maybe define this scene default graphics compositor as an asset? var defaultGraphicsCompositor = GraphicsCompositorHelper.CreateDefault(true, EditorGraphicsCompositorHelper.EditorForwardShadingEffect); // Add UI (engine doesn't depend on it) defaultGraphicsCompositor.RenderFeatures.Add(new UIRenderFeature { RenderStageSelectors = { new SimpleGroupToRenderStageSelector { RenderStage = defaultGraphicsCompositor.RenderStages.First(x => x.Name == "Transparent"), EffectName = "Test", RenderGroup = GizmoBase.DefaultGroupMask } } }); // Make the game switch to this graphics compositor UpdateGraphicsCompositor(defaultGraphicsCompositor); gameContentLoadedTaskSource.SetResult(true); }
private BuildResultCode BuildSlave() { // Mount build path ((FileSystemProvider)VirtualFileSystem.ApplicationData).ChangeBasePath(builderOptions.BuildDirectory); VirtualFileSystem.CreateDirectory(VirtualFileSystem.ApplicationDatabasePath); // Open WCF channel with master builder var namedPipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { SendTimeout = TimeSpan.FromSeconds(300.0), MaxReceivedMessageSize = int.MaxValue }; var processBuilderRemote = ChannelFactory <IProcessBuilderRemote> .CreateChannel(namedPipeBinding, new EndpointAddress(builderOptions.SlavePipe)); try { RegisterRemoteLogger(processBuilderRemote); // Make sure to laod all assemblies containing serializers // TODO: Review how costly it is to do so, and possibily find a way to restrict what needs to be loaded (i.e. only app plugins?) foreach (var assemblyLocation in processBuilderRemote.GetAssemblyContainerLoadedAssemblies()) { AssemblyContainer.Default.LoadAssemblyFromPath(assemblyLocation, builderOptions.Logger); } // Create scheduler var scheduler = new Scheduler(); var status = ResultStatus.NotProcessed; // Schedule command string buildPath = builderOptions.BuildDirectory; Builder.OpenObjectDatabase(buildPath, VirtualFileSystem.ApplicationDatabaseIndexName); var logger = builderOptions.Logger; MicroThread microthread = scheduler.Add(async() => { // Deserialize command and parameters Command command = processBuilderRemote.GetCommandToExecute(); // Run command var inputHashes = FileVersionTracker.GetDefault(); var builderContext = new BuilderContext(inputHashes, null); var commandContext = new RemoteCommandContext(processBuilderRemote, command, builderContext, logger); MicrothreadLocalDatabases.MountDatabase(commandContext.GetOutputObjectsGroups()); command.PreCommand(commandContext); status = await command.DoCommand(commandContext); command.PostCommand(commandContext, status); // Returns result to master builder processBuilderRemote.RegisterResult(commandContext.ResultEntry); }); while (true) { scheduler.Run(); // Exit loop if no more micro threads lock (scheduler.MicroThreads) { if (!scheduler.MicroThreads.Any()) { break; } } Thread.Sleep(0); } // Rethrow any exception that happened in microthread if (microthread.Exception != null) { builderOptions.Logger.Fatal(microthread.Exception.ToString()); return(BuildResultCode.BuildError); } if (status == ResultStatus.Successful || status == ResultStatus.NotTriggeredWasSuccessful) { return(BuildResultCode.Successful); } return(BuildResultCode.BuildError); } finally { // Close WCF channel // ReSharper disable SuspiciousTypeConversion.Global ((IClientChannel)processBuilderRemote).Close(); // ReSharper restore SuspiciousTypeConversion.Global } }
private BuildResultCode BuildSlave() { // Mount build path ((FileSystemProvider)VirtualFileSystem.ApplicationData).ChangeBasePath(builderOptions.BuildDirectory); VirtualFileSystem.CreateDirectory(VirtualFileSystem.ApplicationDatabasePath); // Open ServiceWire Client Channel using (var client = new NpClient <IProcessBuilderRemote>(new NpEndPoint(builderOptions.SlavePipe), new StrideServiceWireSerializer())) { RegisterRemoteLogger(client); // Make sure to laod all assemblies containing serializers // TODO: Review how costly it is to do so, and possibily find a way to restrict what needs to be loaded (i.e. only app plugins?) foreach (var assemblyLocation in client.Proxy.GetAssemblyContainerLoadedAssemblies()) { AssemblyContainer.Default.LoadAssemblyFromPath(assemblyLocation, builderOptions.Logger); } // Create scheduler var scheduler = new Scheduler(); var status = ResultStatus.NotProcessed; // Schedule command string buildPath = builderOptions.BuildDirectory; Builder.OpenObjectDatabase(buildPath, VirtualFileSystem.ApplicationDatabaseIndexName); var logger = builderOptions.Logger; MicroThread microthread = scheduler.Add(async() => { // Deserialize command and parameters Command command = client.Proxy.GetCommandToExecute(); // Run command var inputHashes = FileVersionTracker.GetDefault(); var builderContext = new BuilderContext(inputHashes, null); var commandContext = new RemoteCommandContext(client.Proxy, command, builderContext, logger); MicrothreadLocalDatabases.MountDatabase(commandContext.GetOutputObjectsGroups()); command.PreCommand(commandContext); status = await command.DoCommand(commandContext); command.PostCommand(commandContext, status); // Returns result to master builder client.Proxy.RegisterResult(commandContext.ResultEntry); }); while (true) { scheduler.Run(); // Exit loop if no more micro threads lock (scheduler.MicroThreads) { if (!scheduler.MicroThreads.Any()) { break; } } Thread.Sleep(0); } // Rethrow any exception that happened in microthread if (microthread.Exception != null) { builderOptions.Logger.Fatal(microthread.Exception.ToString()); return(BuildResultCode.BuildError); } if (status == ResultStatus.Successful || status == ResultStatus.NotTriggeredWasSuccessful) { return(BuildResultCode.Successful); } return(BuildResultCode.BuildError); } }
private async Task Execute() { MicrothreadLocalDatabases.MountCommonDatabase(); while (game.IsRunning) { await game.Script.NextFrame(); if (IsActive) { // TODO: code largely duplicated in EditorGameMaterialHighlightService. Factorize! var screenSize = new Vector2(game.GraphicsDevice.Presenter.BackBuffer.Width, game.GraphicsDevice.Presenter.BackBuffer.Height); if (game.Input.IsMouseButtonPressed(MouseButton.Left)) { mouseMoveAccumulator = Vector2.Zero; } mouseMoveAccumulator += new Vector2(Math.Abs(game.Input.MouseDelta.X * screenSize.X), Math.Abs(game.Input.MouseDelta.Y * screenSize.Y)); if (IsMouseAvailable && game.Input.IsMouseButtonReleased(MouseButton.Left) && !game.Input.IsMouseButtonDown(MouseButton.Right)) { if (mouseMoveAccumulator.Length() >= TransformationGizmo.TransformationStartPixelThreshold) { continue; } var addToSelection = game.Input.IsKeyDown(Keys.LeftCtrl) || game.Input.IsKeyDown(Keys.RightCtrl); var entityUnderMouse = Gizmos.GetContentEntityUnderMouse(); if (entityUnderMouse == null) { var entityPicked = Pick(); entityUnderMouse = entityPicked.Entity; } // Ctrl + click on an empty area: do nothing if (entityUnderMouse == null && addToSelection) { continue; } // Click on an empty area: clear selection if (entityUnderMouse == null) { Clear(); } // Click on an entity: select this entity else if (!addToSelection) { Set(entityUnderMouse); } // Ctrl + click on an already selected entity: unselect this entity else if (Contains(entityUnderMouse)) { Remove(entityUnderMouse); } // Ctrl + click on an entity: add this entity to the selection else { Add(entityUnderMouse); } } } } }
protected override Task <bool> Initialize(EditorServiceGame editorGame) { game = (EntityHierarchyEditorGame)editorGame; editorScene = game.EditorScene; var transformMainGizmoRenderStage = new RenderStage("TransformGizmoOpaque", "Main"); var transformTransparentGizmoRenderStage = new RenderStage("TransformGizmoTransparent", "Main") { SortMode = new BackToFrontSortMode() }; game.EditorSceneSystem.GraphicsCompositor.RenderStages.Add(transformMainGizmoRenderStage); game.EditorSceneSystem.GraphicsCompositor.RenderStages.Add(transformTransparentGizmoRenderStage); var meshRenderFeature = game.EditorSceneSystem.GraphicsCompositor.RenderFeatures.OfType <MeshRenderFeature>().First(); // Reset all stages for TransformationGrizmoGroup meshRenderFeature.RenderStageSelectors.Add(new SimpleGroupToRenderStageSelector { RenderGroup = TransformationGizmo.TransformationGizmoGroupMask, }); meshRenderFeature.RenderStageSelectors.Add(new MeshTransparentRenderStageSelector { EffectName = EditorGraphicsCompositorHelper.EditorForwardShadingEffect, RenderGroup = TransformationGizmo.TransformationGizmoGroupMask, OpaqueRenderStage = transformMainGizmoRenderStage, TransparentRenderStage = transformTransparentGizmoRenderStage, }); meshRenderFeature.PipelineProcessors.Add(new MeshPipelineProcessor { TransparentRenderStage = transformTransparentGizmoRenderStage }); var editorCompositor = (EditorTopLevelCompositor)game.EditorSceneSystem.GraphicsCompositor.Game; editorCompositor.PostGizmoCompositors.Add(new ClearRenderer { ClearFlags = ClearRendererFlags.DepthOnly }); editorCompositor.PostGizmoCompositors.Add(new SingleStageRenderer { RenderStage = transformMainGizmoRenderStage, Name = "Transform Opaque Gizmos" }); editorCompositor.PostGizmoCompositors.Add(new SingleStageRenderer { RenderStage = transformTransparentGizmoRenderStage, Name = "Transform Transparent Gizmos" }); TranslationGizmo = new TranslationGizmo(); RotationGizmo = new RotationGizmo(); ScaleGizmo = new ScaleGizmo(); TranslationGizmo.TransformationEnded += OnGizmoTransformationFinished; ScaleGizmo.TransformationEnded += OnGizmoTransformationFinished; RotationGizmo.TransformationEnded += OnGizmoTransformationFinished; transformationGizmos.Add(TranslationGizmo); transformationGizmos.Add(RotationGizmo); transformationGizmos.Add(ScaleGizmo); Services.Get <IEditorGameEntitySelectionService>().SelectionUpdated += UpdateModifiedEntitiesList; // Initialize and add the Gizmo entities to the gizmo scene MicrothreadLocalDatabases.MountCommonDatabase(); // initialize the gizmo foreach (var gizmo in transformationGizmos) { gizmo.Initialize(game.Services, editorScene); } // Deactivate all transformation gizmo by default foreach (var gizmo in transformationGizmos) { gizmo.IsEnabled = false; } // set the default active transformation gizmo ActiveTransformationGizmo = TranslationGizmo; // Start update script (with priority 1 so that it happens after UpdateModifiedEntitiesList is called -- which usually happens from a EditorGameComtroller.PostAction() which has a default priority 0) game.Script.AddTask(Update, 1); return(Task.FromResult(true)); }
/// <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); }
private BuildResultCode BuildSlave() { // Mount build path ((FileSystemProvider)VirtualFileSystem.ApplicationData).ChangeBasePath(builderOptions.BuildDirectory); PrepareDatabases(); VirtualFileSystem.CreateDirectory(VirtualFileSystem.ApplicationDatabasePath); // Open WCF channel with master builder var namedPipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { SendTimeout = TimeSpan.FromSeconds(300.0) }; var processBuilderRemote = ChannelFactory <IProcessBuilderRemote> .CreateChannel(namedPipeBinding, new EndpointAddress(builderOptions.SlavePipe)); try { RegisterRemoteLogger(processBuilderRemote); // Create scheduler var scheduler = new Scheduler(); var status = ResultStatus.NotProcessed; // Schedule command string buildPath = builderOptions.BuildDirectory; string buildProfile = builderOptions.BuildProfile; Builder.OpenObjectDatabase(buildPath, VirtualFileSystem.ApplicationDatabaseIndexName); var logger = builderOptions.Logger; MicroThread microthread = scheduler.Add(async() => { // Deserialize command and parameters Command command = processBuilderRemote.GetCommandToExecute(); BuildParameterCollection parameters = processBuilderRemote.GetBuildParameters(); // Run command var inputHashes = FileVersionTracker.GetDefault(); var builderContext = new BuilderContext(buildPath, buildProfile, inputHashes, parameters, 0, null); var commandContext = new RemoteCommandContext(processBuilderRemote, command, builderContext, logger); MicrothreadLocalDatabases.MountDatabase(commandContext.GetOutputObjectsGroups()); command.PreCommand(commandContext); status = await command.DoCommand(commandContext); command.PostCommand(commandContext, status); // Returns result to master builder processBuilderRemote.RegisterResult(commandContext.ResultEntry); }); while (true) { scheduler.Run(); // Exit loop if no more micro threads lock (scheduler.MicroThreads) { if (!scheduler.MicroThreads.Any()) { break; } } Thread.Sleep(0); } // Rethrow any exception that happened in microthread if (microthread.Exception != null) { builderOptions.Logger.Fatal(microthread.Exception.ToString()); return(BuildResultCode.BuildError); } if (status == ResultStatus.Successful || status == ResultStatus.NotTriggeredWasSuccessful) { return(BuildResultCode.Successful); } return(BuildResultCode.BuildError); } finally { // Close WCF channel // ReSharper disable SuspiciousTypeConversion.Global ((IClientChannel)processBuilderRemote).Close(); // ReSharper restore SuspiciousTypeConversion.Global } }