/// <summary> /// Performs pipeline initialization, enumerates views and populates visibility groups. /// </summary> /// <param name="context"></param> public void Collect(RenderThreadContext context) { foreach (var renderFeature in RenderFeatures) { renderFeature.Collect(); } }
/// <param name="context"></param> /// <inheritdoc/> public override void PrepareEffectPermutations(RenderThreadContext context) { var renderEffects = RootRenderFeature.RenderData.GetData(renderEffectKey); int effectSlotCount = ((RootEffectRenderFeature)RootRenderFeature).EffectPermutationSlotCount; foreach (var renderObject in RootRenderFeature.RenderObjects) { var staticObjectNode = renderObject.StaticObjectNode; for (int i = 0; i < effectSlotCount; ++i) { var staticEffectObjectNode = staticObjectNode * effectSlotCount + i; var renderEffect = renderEffects[staticEffectObjectNode]; var renderMesh = (RenderMesh)renderObject; // Skip effects not used during this frame if (renderEffect == null || !renderEffect.IsUsedDuringThisFrame(RenderSystem)) { continue; } if (renderMesh.Mesh.Skinning != null) { renderEffect.EffectValidator.ValidateParameter(MaterialKeys.HasSkinningPosition, renderMesh.Mesh.Parameters.Get(MaterialKeys.HasSkinningPosition)); renderEffect.EffectValidator.ValidateParameter(MaterialKeys.HasSkinningNormal, renderMesh.Mesh.Parameters.Get(MaterialKeys.HasSkinningNormal)); renderEffect.EffectValidator.ValidateParameter(MaterialKeys.HasSkinningTangent, renderMesh.Mesh.Parameters.Get(MaterialKeys.HasSkinningTangent)); var skinningBones = Math.Max(MaxBones, renderMesh.Mesh.Skinning.Bones.Length); renderEffect.EffectValidator.ValidateParameter(MaterialKeys.SkinningMaxBones, skinningBones); } } } }
/// <inheritdoc/> public override unsafe void Prepare(RenderThreadContext context) { var renderModelObjectInfoData = RootRenderFeature.RenderData.GetData(renderModelObjectInfoKey); foreach (var renderNode in ((RootEffectRenderFeature)RootRenderFeature).RenderNodes) { var perDrawLayout = renderNode.RenderEffect.Reflection.PerDrawLayout; if (perDrawLayout == null) { continue; } var blendMatricesOffset = perDrawLayout.GetConstantBufferOffset(blendMatrices); if (blendMatricesOffset == -1) { continue; } var renderModelObjectInfo = renderModelObjectInfoData[renderNode.RenderObject.ObjectNode]; var mappedCB = renderNode.Resources.ConstantBuffer.Data + blendMatricesOffset; var blendMatrix = (Matrix *)mappedCB; for (int i = 0; i < renderModelObjectInfo.NodeInfoCount; i++) { int boneInfoIndex = renderModelObjectInfo.NodeInfoOffset + i; Matrix.Multiply(ref nodeInfos.Items[boneInfoIndex].LinkToMeshMatrix, ref nodeInfos.Items[boneInfoIndex].NodeTransformation, out *blendMatrix++); } } }
/// <inheritdoc/> public override void Prepare(RenderThreadContext context) { base.Prepare(context); // Prepare each sub render feature foreach (var renderFeature in RenderFeatures) { renderFeature.Prepare(context); } }
/// <param name="context"></param> /// <inheritdoc/> public override void PrepareEffectPermutationsImpl(RenderThreadContext context) { // Setup ActiveMeshDraw foreach (var objectNodeReference in ObjectNodeReferences) { var objectNode = GetObjectNode(objectNodeReference); var renderMesh = (RenderMesh)objectNode.RenderObject; renderMesh.ActiveMeshDraw = renderMesh.Mesh.Draw; } base.PrepareEffectPermutationsImpl(context); foreach (var renderFeature in RenderFeatures) { renderFeature.PrepareEffectPermutations(context); } }
/// <summary> /// Performs most of the work (computation and resource preparation). Later game simulation might be running during that step. /// </summary> /// <param name="context"></param> public unsafe void Prepare(RenderThreadContext context) { // Sync point: after extract, before prepare (game simulation could resume now) // Generate and execute prepare effect jobs foreach (var renderFeature in RenderFeatures) // We might be able to parallelize too as long as we resepect render feature dependency graph (probably very few dependencies in practice) { // Divide into task chunks for parallelism renderFeature.PrepareEffectPermutations(context); } // Generate and execute prepare jobs foreach (var renderFeature in RenderFeatures) // We might be able to parallelize too as long as we resepect render feature dependency graph (probably very few dependencies in practice) { // Divide into task chunks for parallelism renderFeature.Prepare(context); } // Sort foreach (var view in Views) { foreach (var renderViewStage in view.RenderStages) { var renderNodes = renderViewStage.RenderNodes; if (renderNodes.Count == 0) { continue; } var renderStage = renderViewStage.RenderStage; // Allocate sorted render nodes if (renderViewStage.SortedRenderNodes == null || renderViewStage.SortedRenderNodes.Length < renderNodes.Count) { Array.Resize(ref renderViewStage.SortedRenderNodes, renderNodes.Count); } var sortedRenderNodes = renderViewStage.SortedRenderNodes; if (renderStage.SortMode != null) { // Make sure sortKeys is big enough if (sortKeys == null || sortKeys.Length < renderNodes.Count) Array.Resize(ref sortKeys, renderNodes.Count); // renderNodes[start..end] belongs to the same render feature fixed(SortKey *sortKeysPtr = sortKeys) renderStage.SortMode.GenerateSortKey(view, renderViewStage, sortKeysPtr); Array.Sort(sortKeys, 0, renderNodes.Count); // Reorder list for (int i = 0; i < renderNodes.Count; ++i) { sortedRenderNodes[i] = renderNodes[sortKeys[i].Index]; } } else { // No sorting, copy as is for (int i = 0; i < renderNodes.Count; ++i) { sortedRenderNodes[i] = renderNodes[i]; } } } } }
/// <summary> /// Extract data from entities, should be as fast as possible to not block simulation loop. It should be mostly copies, and the actual processing should be part of Prepare(). /// </summary> public void Extract(RenderThreadContext context) { // Prepare views for (int index = 0; index < Views.Count; index++) { // Update indices var view = Views[index]; view.Index = index; // Create missing RenderViewFeature while (view.Features.Count < RenderFeatures.Count) { view.Features.Add(new RenderViewFeature()); } for (int i = 0; i < RenderFeatures.Count; i++) { var renderViewFeature = view.Features[i]; renderViewFeature.RootFeature = RenderFeatures[i]; } } // Create nodes for objects to render foreach (var view in Views) { // Sort per render feature (used for later sorting) // We'll be able to process data more efficiently for the next steps view.RenderObjects.Sort(RenderObjectFeatureComparer.Default); foreach (var renderObject in view.RenderObjects) { var renderFeature = renderObject.RenderFeature; var viewFeature = view.Features[renderFeature.Index]; // Create object node renderFeature.GetOrCreateObjectNode(renderObject); // Let's create the view object node var renderViewNode = renderFeature.CreateViewObjectNode(view, renderObject); viewFeature.ViewObjectNodes.Add(renderViewNode); // Collect object // TODO: Check which stage it belongs to (and skip everything if it doesn't belong to any stage) // TODO: For now, we build list and then copy. Another way would be to count and then fill (might be worse, need to check) var activeRenderStages = renderObject.ActiveRenderStages; foreach (var renderViewStage in view.RenderStages) { // Check if this RenderObject wants to be rendered for this render stage var renderStageIndex = renderViewStage.RenderStage.Index; if (!activeRenderStages[renderStageIndex].Active) { continue; } var renderNode = renderFeature.CreateRenderNode(renderObject, view, renderViewNode, renderViewStage.RenderStage); // Note: Used mostly during updating viewFeature.RenderNodes.Add(renderNode); // Note: Used mostly during rendering renderViewStage.RenderNodes.Add(new RenderNodeFeatureReference(renderFeature, renderNode, renderObject)); } } // Also sort view|stage per render feature foreach (var renderViewStage in view.RenderStages) { renderViewStage.RenderNodes.Sort(RenderNodeFeatureReferenceComparer.Default); } } // Ensure size of data arrays per objects PrepareDataArrays(); // Generate and execute extract jobs foreach (var renderFeature in RenderFeatures) // We might be able to parallelize too as long as we resepect render feature dependency graph (probably very few dependencies in practice) { // Divide into task chunks for parallelism renderFeature.Extract(); } // Ensure size of all other data arrays PrepareDataArrays(); }
/// <inheritdoc/> public override void Prepare(RenderThreadContext context) { EffectObjectNodes.Clear(); // Make sure descriptor set pool is large enough var expectedDescriptorSetPoolSize = RenderNodes.Count * effectDescriptorSetSlots.Count; if (ResourceGroupPool.Length < expectedDescriptorSetPoolSize) { Array.Resize(ref ResourceGroupPool, expectedDescriptorSetPoolSize); } // Allocate PerFrame, PerView and PerDraw resource groups and constant buffers var renderEffects = RenderData.GetData(RenderEffectKey); int effectSlotCount = EffectPermutationSlotCount; foreach (var view in RenderSystem.Views) { var viewFeature = view.Features[Index]; foreach (var renderNodeReference in viewFeature.RenderNodes) { var renderNode = this.GetRenderNode(renderNodeReference); var renderObject = renderNode.RenderObject; // Get RenderEffect var staticObjectNode = renderObject.StaticObjectNode; var staticEffectObjectNode = staticObjectNode * effectSlotCount + effectSlots[renderNode.RenderStage.Index].Index; var renderEffect = renderEffects[staticEffectObjectNode]; // Not compiled yet? if (renderEffect.Effect == null) { renderNode.RenderEffect = renderEffect; renderNode.EffectObjectNode = EffectObjectNodeReference.Invalid; renderNode.Resources = null; RenderNodes[renderNodeReference.Index] = renderNode; continue; } var renderEffectReflection = renderEffect.Reflection; // PerView resources/cbuffer var viewLayout = renderEffectReflection.PerViewLayout; if (viewLayout != null) { if (viewLayout.Entries?.Length <= view.Index) { var oldEntries = viewLayout.Entries; viewLayout.Entries = new ResourceGroupEntry[RenderSystem.Views.Count]; for (int index = 0; index < oldEntries.Length; index++) { viewLayout.Entries[index] = oldEntries[index]; } for (int index = oldEntries.Length; index < viewLayout.Entries.Length; index++) { viewLayout.Entries[index].Resources = new ResourceGroup(); } } if (viewLayout.Entries[view.Index].MarkAsUsed(RenderSystem)) { context.ResourceGroupAllocator.PrepareResourceGroup(viewLayout, BufferPoolAllocationType.UsedMultipleTime, viewLayout.Entries[view.Index].Resources); // Register it in list of view layouts to update for this frame viewFeature.Layouts.Add(viewLayout); } } // PerFrame resources/cbuffer var frameLayout = renderEffect.Reflection.PerFrameLayout; if (frameLayout != null && frameLayout.Entry.MarkAsUsed(RenderSystem)) { context.ResourceGroupAllocator.PrepareResourceGroup(frameLayout, BufferPoolAllocationType.UsedMultipleTime, frameLayout.Entry.Resources); // Register it in list of view layouts to update for this frame FrameLayouts.Add(frameLayout); } // PerDraw resources/cbuffer // Get nodes var viewObjectNode = GetViewObjectNode(renderNode.ViewObjectNode); // Allocate descriptor set renderNode.Resources = context.ResourceGroupAllocator.AllocateResourceGroup(); if (renderEffectReflection.PerDrawLayout != null) { context.ResourceGroupAllocator.PrepareResourceGroup(renderEffectReflection.PerDrawLayout, BufferPoolAllocationType.UsedOnce, renderNode.Resources); } // Link to EffectObjectNode (created right after) // TODO: rewrite this renderNode.EffectObjectNode = new EffectObjectNodeReference(EffectObjectNodes.Count); renderNode.RenderEffect = renderEffect; // Bind well-known descriptor sets var descriptorSetPoolOffset = ComputeResourceGroupOffset(renderNodeReference); ResourceGroupPool[descriptorSetPoolOffset + perFrameDescriptorSetSlot.Index] = frameLayout?.Entry.Resources; ResourceGroupPool[descriptorSetPoolOffset + perViewDescriptorSetSlot.Index] = renderEffect.Reflection.PerViewLayout?.Entries[view.Index].Resources; ResourceGroupPool[descriptorSetPoolOffset + perDrawDescriptorSetSlot.Index] = renderNode.Resources; // Create resource group for everything else in case of fallback effects if (renderEffect.State != RenderEffectState.Normal && renderEffect.FallbackParameters != null) { if (renderEffect.FallbackParameterUpdater.ResourceGroups == null) { // First time renderEffect.FallbackParameterUpdater = new EffectParameterUpdater(renderEffect.Reflection.FallbackUpdaterLayout, renderEffect.FallbackParameters); } renderEffect.FallbackParameterUpdater.Update(RenderSystem.GraphicsDevice, context.ResourceGroupAllocator, renderEffect.FallbackParameters); var fallbackResourceGroupMapping = renderEffect.Reflection.FallbackResourceGroupMapping; for (int i = 0; i < fallbackResourceGroupMapping.Length; ++i) { ResourceGroupPool[descriptorSetPoolOffset + fallbackResourceGroupMapping[i]] = renderEffect.FallbackParameterUpdater.ResourceGroups[i]; } } // Compile pipeline state object (if first time or need change) // TODO GRAPHICS REFACTOR how to invalidate if we want to change some state? (setting to null should be fine) if (renderEffect.PipelineState == null) { var pipelineState = MutablePipeline.State; pipelineState.SetDefaults(); // Effect pipelineState.EffectBytecode = renderEffect.Effect.Bytecode; pipelineState.RootSignature = renderEffect.Reflection.RootSignature; // Extract outputs from render stage pipelineState.Output = renderNode.RenderStage.Output; // Bind VAO ProcessPipelineState(Context, renderNodeReference, ref renderNode, renderObject, pipelineState); PostProcessPipelineState?.Invoke(renderNodeReference, ref renderNode, renderObject, pipelineState); MutablePipeline.Update(); renderEffect.PipelineState = MutablePipeline.CurrentState; } RenderNodes[renderNodeReference.Index] = renderNode; // Create EffectObjectNode EffectObjectNodes.Add(new EffectObjectNode(renderEffect, viewObjectNode.ObjectNode)); } } }
/// <param name="context"></param> /// <inheritdoc/> public override void PrepareEffectPermutations(RenderThreadContext context) { base.PrepareEffectPermutations(context); // TODO: Temporary until we have a better system for handling permutations var renderEffects = RenderData.GetData(RenderEffectKey); int effectSlotCount = EffectPermutationSlotCount; foreach (var view in RenderSystem.Views) { var viewFeature = view.Features[Index]; foreach (var renderNodeReference in viewFeature.RenderNodes) { var renderNode = this.GetRenderNode(renderNodeReference); var renderObject = renderNode.RenderObject; // Get RenderEffect var staticObjectNode = renderObject.StaticObjectNode; var staticEffectObjectNode = staticObjectNode * effectSlotCount + effectSlots[renderNode.RenderStage.Index].Index; var renderEffect = renderEffects[staticEffectObjectNode]; var effectSelector = renderObject.ActiveRenderStages[renderNode.RenderStage.Index].EffectSelector; // Create it (first time) or regenerate it if effect changed if (renderEffect == null || effectSelector != renderEffect.EffectSelector) { renderEffect = new RenderEffect(renderObject.ActiveRenderStages[renderNode.RenderStage.Index].EffectSelector); renderEffects[staticEffectObjectNode] = renderEffect; } // Is it the first time this frame that we check this RenderEffect? if (renderEffect.MarkAsUsed(RenderSystem)) { renderEffect.EffectValidator.BeginEffectValidation(); } } } // Step1: Perform permutations PrepareEffectPermutationsImpl(context); // CompilerParameters are ThreadStatic if (staticCompilerParameters == null) { staticCompilerParameters = new CompilerParameters(); } // Step2: Compile effects and update reflection infos (offset, etc...) foreach (var renderObject in RenderObjects) { var staticObjectNode = renderObject.StaticObjectNode; for (int i = 0; i < effectSlotCount; ++i) { var staticEffectObjectNode = staticObjectNode * effectSlotCount + i; var renderEffect = renderEffects[staticEffectObjectNode]; // Skip if not used if (renderEffect == null || !renderEffect.IsUsedDuringThisFrame(RenderSystem)) { continue; } // Skip if nothing changed Effect effect; if (renderEffect.EffectValidator.ShouldSkip) { // Reset pending effect, as it is now obsolete anyway effect = null; renderEffect.Effect = null; renderEffect.State = RenderEffectState.Skip; } else if (renderEffect.EffectValidator.EndEffectValidation()) { InvalidateEffectPermutation(renderObject, renderEffect); // Still, let's check if there is a pending effect compiling var pendingEffect = renderEffect.PendingEffect; if (pendingEffect == null || !pendingEffect.IsCompleted) { continue; } renderEffect.ClearFallbackParameters(); if (pendingEffect.IsFaulted) { renderEffect.State = RenderEffectState.Error; effect = ComputeFallbackEffect?.Invoke(renderObject, renderEffect, RenderEffectState.Error); } else { renderEffect.State = RenderEffectState.Normal; effect = pendingEffect.Result; } renderEffect.PendingEffect = null; } else { // Reset pending effect, as it is now obsolete anyway renderEffect.PendingEffect = null; renderEffect.State = RenderEffectState.Normal; foreach (var effectValue in renderEffect.EffectValidator.EffectValues) { staticCompilerParameters.SetObject(effectValue.Key, effectValue.Value); } var asyncEffect = RenderSystem.EffectSystem.LoadEffect(renderEffect.EffectSelector.EffectName, staticCompilerParameters); staticCompilerParameters.Clear(); effect = asyncEffect.Result; if (effect == null) { // Effect still compiling, let's find if there is a fallback renderEffect.ClearFallbackParameters(); effect = ComputeFallbackEffect?.Invoke(renderObject, renderEffect, RenderEffectState.Compiling); if (effect != null) { // Use the fallback for now renderEffect.PendingEffect = asyncEffect.Task; renderEffect.State = RenderEffectState.Compiling; } else { // No fallback effect, let's block until effect is compiled effect = asyncEffect.WaitForResult(); } } } var effectHashCode = effect != null ? (uint)effect.GetHashCode() : 0; // Effect is last 16 bits renderObject.StateSortKey = (renderObject.StateSortKey & 0xFFFF0000) | (effectHashCode & 0x0000FFFF); if (effect != null) { RenderEffectReflection renderEffectReflection; if (!InstantiatedEffects.TryGetValue(effect, out renderEffectReflection)) { renderEffectReflection = new RenderEffectReflection(); // Build root signature automatically from reflection renderEffectReflection.DescriptorReflection = EffectDescriptorSetReflection.New(RenderSystem.GraphicsDevice, effect.Bytecode, effectDescriptorSetSlots, "PerFrame"); renderEffectReflection.ResourceGroupDescriptions = new ResourceGroupDescription[renderEffectReflection.DescriptorReflection.Layouts.Count]; // Compute ResourceGroup hashes for (int index = 0; index < renderEffectReflection.DescriptorReflection.Layouts.Count; index++) { var descriptorSet = renderEffectReflection.DescriptorReflection.Layouts[index]; if (descriptorSet.Layout == null) { continue; } var constantBufferReflection = effect.Bytecode.Reflection.ConstantBuffers.FirstOrDefault(x => x.Name == descriptorSet.Name); renderEffectReflection.ResourceGroupDescriptions[index] = new ResourceGroupDescription(descriptorSet.Layout, constantBufferReflection); } renderEffectReflection.RootSignature = RootSignature.New(RenderSystem.GraphicsDevice, renderEffectReflection.DescriptorReflection); renderEffectReflection.BufferUploader.Compile(RenderSystem.GraphicsDevice, renderEffectReflection.DescriptorReflection, effect.Bytecode); // Prepare well-known descriptor set layouts renderEffectReflection.PerDrawLayout = CreateDrawResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perDrawDescriptorSetSlot.Index], effect.Bytecode); renderEffectReflection.PerFrameLayout = CreateFrameResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perFrameDescriptorSetSlot.Index], effect.Bytecode); renderEffectReflection.PerViewLayout = CreateViewResourceGroupLayout(renderEffectReflection.ResourceGroupDescriptions[perViewDescriptorSetSlot.Index], effect.Bytecode); InstantiatedEffects.Add(effect, renderEffectReflection); // Notify a new effect has been compiled EffectCompiled?.Invoke(RenderSystem, effect, renderEffectReflection); } // Setup fallback parameters if (renderEffect.State != RenderEffectState.Normal && renderEffectReflection.FallbackUpdaterLayout == null) { // Process all "non standard" layouts var layoutMapping = new int[renderEffectReflection.DescriptorReflection.Layouts.Count - 3]; var layouts = new DescriptorSetLayoutBuilder[renderEffectReflection.DescriptorReflection.Layouts.Count - 3]; int layoutMappingIndex = 0; for (int index = 0; index < renderEffectReflection.DescriptorReflection.Layouts.Count; index++) { var layout = renderEffectReflection.DescriptorReflection.Layouts[index]; // Skip well-known layouts (already handled) if (layout.Name == "PerDraw" || layout.Name == "PerFrame" || layout.Name == "PerView") { continue; } layouts[layoutMappingIndex] = layout.Layout; layoutMapping[layoutMappingIndex++] = index; } renderEffectReflection.FallbackUpdaterLayout = new EffectParameterUpdaterLayout(RenderSystem.GraphicsDevice, effect, layouts); renderEffectReflection.FallbackResourceGroupMapping = layoutMapping; } // Update effect renderEffect.Effect = effect; renderEffect.Reflection = renderEffectReflection; // Invalidate pipeline state (new effect) renderEffect.PipelineState = null; renderEffects[staticEffectObjectNode] = renderEffect; } else { renderEffect.Reflection = RenderEffectReflection.Empty; renderEffect.PipelineState = null; } } } }
/// <summary> /// Actual implementation of <see cref="PrepareEffectPermutations"/>. /// </summary> /// <param name="context"></param> public virtual void PrepareEffectPermutationsImpl(RenderThreadContext context) { }
/// <summary> /// Performs most of the work (computation and resource preparation). Later game simulation might be running during that step. /// </summary> /// <param name="context"></param> public virtual void Prepare(RenderThreadContext context) { }
/// <param name="context"></param> /// <inheritdoc/> public unsafe override void Prepare(RenderThreadContext context) { // Compute WorldView, WorldViewProj var renderModelObjectInfoData = RootRenderFeature.RenderData.GetData(renderModelObjectInfoKey); var renderModelViewInfoData = RootRenderFeature.RenderData.GetData(renderModelViewInfoKey); // Update PerFrame (time) // TODO Move that to RootEffectRenderFeature? foreach (var frameLayout in ((RootEffectRenderFeature)RootRenderFeature).FrameLayouts) { var timeOffset = frameLayout.GetConstantBufferOffset(time); if (timeOffset == -1) { continue; } var resourceGroup = frameLayout.Entry.Resources; var mappedCB = resourceGroup.ConstantBuffer.Data; var perFrameTime = (PerFrameTime *)((byte *)mappedCB + timeOffset); perFrameTime->Time = (float)Context.Time.Total.TotalSeconds; perFrameTime->TimeStep = (float)Context.Time.Elapsed.TotalSeconds; } // Update PerView (View, Proj, etc...) for (int index = 0; index < RenderSystem.Views.Count; index++) { var view = RenderSystem.Views[index]; var viewFeature = view.Features[RootRenderFeature.Index]; // Compute WorldView and WorldViewProjection foreach (var renderPerViewNodeReference in viewFeature.ViewObjectNodes) { var renderPerViewNode = RootRenderFeature.GetViewObjectNode(renderPerViewNodeReference); var renderModelFrameInfo = renderModelObjectInfoData[renderPerViewNode.ObjectNode]; var renderModelViewInfo = new RenderModelViewInfo(); Matrix.Multiply(ref renderModelFrameInfo.World, ref view.View, out renderModelViewInfo.WorldView); Matrix.Multiply(ref renderModelFrameInfo.World, ref view.ViewProjection, out renderModelViewInfo.WorldViewProjection); // TODO: Use ref locals or Utilities instead, to avoid double copy renderModelViewInfoData[renderPerViewNodeReference] = renderModelViewInfo; } // Copy ViewProjection to PerView cbuffer foreach (var viewLayout in viewFeature.Layouts) { var viewProjectionOffset = viewLayout.GetConstantBufferOffset(this.view); if (viewProjectionOffset == -1) { continue; } var resourceGroup = viewLayout.Entries[view.Index].Resources; var mappedCB = resourceGroup.ConstantBuffer.Data; var perView = (PerView *)((byte *)mappedCB + viewProjectionOffset); // Fill PerView perView->View = view.View; Matrix.Invert(ref view.View, out perView->ViewInverse); perView->Projection = view.Projection; Matrix.Invert(ref view.Projection, out perView->ProjectionInverse); perView->ViewProjection = view.ViewProjection; perView->ProjScreenRay = new Vector2(-1.0f / view.Projection.M11, 1.0f / view.Projection.M22); // TODO GRAPHICS REFACTOR avoid cbuffer read perView->Eye = new Vector4(perView->ViewInverse.M41, perView->ViewInverse.M42, perView->ViewInverse.M43, 1.0f); } // Copy Camera to PerView cbuffer var cameraComponent = view.Camera; if (cameraComponent != null) { foreach (var viewLayout in viewFeature.Layouts) { var cameraOffset = viewLayout.GetConstantBufferOffset(camera); if (cameraOffset == -1) { continue; } var resourceGroup = viewLayout.Entries[view.Index].Resources; var mappedCB = resourceGroup.ConstantBuffer.Data; var perViewCamera = (PerViewCamera *)((byte *)mappedCB + cameraOffset); perViewCamera->NearClipPlane = cameraComponent.NearClipPlane; perViewCamera->FarClipPlane = cameraComponent.FarClipPlane; perViewCamera->ZProjection = CameraKeys.ZProjectionACalculate(cameraComponent.NearClipPlane, cameraComponent.FarClipPlane); if (view.SceneCameraRenderer != null) { perViewCamera->ViewSize = new Vector2(view.SceneCameraRenderer.ComputedViewport.Width, view.SceneCameraRenderer.ComputedViewport.Height); } perViewCamera->AspectRatio = cameraComponent.AspectRatio; perViewCamera->VerticalFieldOfView = cameraComponent.VerticalFieldOfView; perViewCamera->OrthoSize = cameraComponent.OrthographicSize; } } } // Update PerDraw (World, WorldViewProj, etc...) // Copy Entity.World to PerDraw cbuffer // TODO: Have a PerObject cbuffer? foreach (var renderNode in ((RootEffectRenderFeature)RootRenderFeature).RenderNodes) { var perDrawLayout = renderNode.RenderEffect.Reflection.PerDrawLayout; if (perDrawLayout == null) { continue; } var worldOffset = perDrawLayout.GetConstantBufferOffset(this.world); if (worldOffset == -1) { continue; } var renderModelObjectInfo = renderModelObjectInfoData[renderNode.RenderObject.ObjectNode]; var renderModelViewInfo = renderModelViewInfoData[renderNode.ViewObjectNode]; var mappedCB = renderNode.Resources.ConstantBuffer.Data; var perDraw = (PerDraw *)((byte *)mappedCB + worldOffset); // Fill PerDraw perDraw->World = renderModelObjectInfo.World; Matrix.Invert(ref renderModelObjectInfo.World, out perDraw->WorldInverse); // TODO GRAPHICS REFACTOR avoid cbuffer read Matrix.Transpose(ref perDraw->WorldInverse, out perDraw->WorldInverseTranspose); perDraw->WorldView = renderModelViewInfo.WorldView; Matrix.Invert(ref renderModelViewInfo.WorldView, out perDraw->WorldViewInverse); perDraw->WorldViewProjection = renderModelViewInfo.WorldViewProjection; perDraw->WorldScale = new Vector3( ((Vector3)renderModelObjectInfo.World.Row1).Length(), ((Vector3)renderModelObjectInfo.World.Row2).Length(), ((Vector3)renderModelObjectInfo.World.Row3).Length()); // TODO GRAPHICS REFACTOR avoid cbuffer read perDraw->EyeMS = new Vector4(perDraw->WorldViewInverse.M41, perDraw->WorldViewInverse.M42, perDraw->WorldViewInverse.M43, 1.0f); } }