/// <summary>
        /// Allocates global buffers.
        /// </summary>
        private void OnCapacityCountChange()
        {
            // Allocate global buffers
            bool allocateGlobalBuffer = false;

            if (globalBuffer == null)
            {
                allocateGlobalBuffer = true;
            }
            else if (globalBuffer.ElementCount != CapacityCount)
            {
                // Reallocate global buffer if capacity changed
                allocateGlobalBuffer = true;
                // Release previous buffer
                globalBuffer.Release();
                sortBuffer.Release();
            }

            // Allocate new buffers
            if (allocateGlobalBuffer)
            {
                if (CapacityCount > 0)
                {
                    globalBuffer      = Buffer.Structured.New(graphicsDeviceService.GraphicsDevice, CapacityCount, StructureSize, true);
                    globalBuffer.Name = "ParticleGlobalBuffer";
                    sortBuffer        = Buffer.Structured.New(graphicsDeviceService.GraphicsDevice, CapacityCount, sizeof(int) * 2, true);
                    sortBuffer.Name   = "ParticleSortBuffer";
                }
            }
        }
Exemple #2
0
 public void Dispose()
 {
     if (GeneratedIndicesAEN != null)
     {
         GeneratedIndicesAEN.Dispose();
         GeneratedIndicesAEN = null;
     }
 }
 public void ReleaseBuffers(RenderDrawContext renderDrawContext)
 {
     // Release the temporary vertex buffer
     if (VertexBuffer != null)
     {
         renderDrawContext.GraphicsContext.Allocator.ReleaseReference(VertexBuffer);
         VertexBuffer = null;
     }
 }
Exemple #4
0
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public override void Dispose()
        {
            if (VertexBuffer != null && !VertexBuffer.IsDisposed)
            {
                VertexBuffer.Dispose();
            }

            if (effect != null && !effect.IsDisposed)
            {
                effect.Dispose();
            }
        }
            public DeviceResourceContext(GraphicsDevice device, VertexDeclaration declaration, int vertexCount, int indexStructSize, int indexCount)
            {
                var vertexSize = declaration.CalculateSize();

                VertexCount = vertexCount;
                IndexCount  = indexCount;

                VertexBuffer = Buffer.Vertex.New(device, VertexCount * vertexSize, GraphicsResourceUsage.Dynamic).DisposeBy(this);

                IndexBuffer = Buffer.Index.New(device, IndexCount * indexStructSize, GraphicsResourceUsage.Dynamic).DisposeBy(this);

                dirty = true;
            }
            public void DisposeBuffers()
            {
                if (AppendBuffer != null)
                {
                    AppendBuffer.Release();
                    AppendBuffer = null;
                }

                if (ConsumeBuffer != null)
                {
                    ConsumeBuffer.Release();
                    ConsumeBuffer = null;
                }
            }
Exemple #7
0
        /// <inheritdoc/>
        protected override void InitializeCore()
        {
            base.InitializeCore();

            RenderFeatures.CollectionChanged += RenderFeatures_CollectionChanged;

            foreach (var renderFeature in RenderFeatures)
            {
                renderFeature.AttachRootRenderFeature(this);
                renderFeature.Initialize(Context);
            }

            // Create an empty buffer to compensate for missing vertex streams
            emptyBuffer = Buffer.Vertex.New(Context.GraphicsDevice, new Vector4[1]);
        }
Exemple #8
0
        protected override void Destroy()
        {
            foreach (var renderFeature in RenderFeatures)
            {
                renderFeature.Dispose();
            }

            RenderFeatures.CollectionChanged -= RenderFeatures_CollectionChanged;

            descriptorSets.Dispose();

            emptyBuffer?.Dispose();
            emptyBuffer = null;

            base.Destroy();
        }
        private static byte[] GetDataSafe(this Buffer buffer, CommandList commandList = null)
        {
            var data = buffer.GetSerializationData();

            if (data != null)
            {
                return(data.Content);
            }

            if (commandList == null)
            {
                throw new InvalidOperationException("Could not find underlying CPU buffer data and no command list was given to extract them from GPU");
            }

            return(buffer.GetData <byte>(commandList));
        }
        public override void Unload()
        {
            // Dispose GPU resources
            lightClusters?.Dispose();
            lightClusters = null;

            lightIndicesBuffer?.Dispose();
            lightIndicesBuffer = null;

            pointLightsBuffer?.Dispose();
            pointLightsBuffer = null;

            spotLightsBuffer?.Dispose();
            spotLightsBuffer = null;

            base.Unload();
        }
        public ParameterConstantBuffer(GraphicsDevice device, string constantBufferName, ShaderConstantBufferDescription constantBufferDesc)
        {
            ConstantBufferDesc = constantBufferDesc;
            constantBufferDatas = new ConstantBufferData[GraphicsDevice.ThreadCount];
            dataStreams = new DataPointer[GraphicsDevice.ThreadCount];

            for (uint i = 0; i < GraphicsDevice.ThreadCount; ++i)
            {
                constantBufferDatas[i] = new ConstantBufferData(constantBufferDesc);
                dataStreams[i] = new DataPointer(constantBufferDatas[i].Data, constantBufferDesc.Size);
            }

            Buffer = SiliconStudio.Xenko.Graphics.Buffer.New(device, constantBufferDatas[0].Desc.Size, BufferFlags.ConstantBuffer, UsingMap ? GraphicsResourceUsage.Dynamic : GraphicsResourceUsage.Default);
            
            // We want to clear flags
            // TODO: Should be later replaced with either an internal field on GraphicsResourceBase, or a reset counter somewhere?
            Buffer.Reload = Reload;
        }
        public ParameterConstantBuffer(GraphicsDevice device, string constantBufferName, ShaderConstantBufferDescription constantBufferDesc)
        {
            ConstantBufferDesc  = constantBufferDesc;
            constantBufferDatas = new ConstantBufferData[GraphicsDevice.ThreadCount];
            dataStreams         = new DataPointer[GraphicsDevice.ThreadCount];

            for (uint i = 0; i < GraphicsDevice.ThreadCount; ++i)
            {
                constantBufferDatas[i] = new ConstantBufferData(constantBufferDesc);
                dataStreams[i]         = new DataPointer(constantBufferDatas[i].Data, constantBufferDesc.Size);
            }

            Buffer = SiliconStudio.Xenko.Graphics.Buffer.New(device, constantBufferDatas[0].Desc.Size, BufferFlags.ConstantBuffer, UsingMap ? GraphicsResourceUsage.Dynamic : GraphicsResourceUsage.Default);

            // We want to clear flags
            // TODO: Should be later replaced with either an internal field on GraphicsResourceBase, or a reset counter somewhere?
            Buffer.Reload = Reload;
        }
Exemple #13
0
        /// <summary>
        /// Creates an Entity that contains our dynamic Vertex and Index buffers.
        /// This Entity will be rendered by the model renderer.
        /// </summary>
        /// <param name="verticesCount"></param>
        /// <param name="indicesCount"></param>
        private void CreateTerrainModelEntity(int verticesCount, int indicesCount)
        {
            // Compute sizes
            var vertexDeclaration = VertexNormalTexture.VertexDeclaration;
            var vertexBufferSize  = verticesCount * vertexDeclaration.CalculateSize();
            var indexBufferSize   = indicesCount * sizeof(short);

            // Create Vertex and Index buffers
            terrainVertexBuffer = Buffer.Vertex.New(GraphicsDevice, vertexBufferSize, GraphicsResourceUsage.Dynamic);
            terrainIndexBuffer  = Buffer.New(GraphicsDevice, indexBufferSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic);

            // Prepare mesh and entity
            var meshDraw = new MeshDraw
            {
                PrimitiveType = PrimitiveType.TriangleStrip,
                VertexBuffers = new[] { new VertexBufferBinding(terrainVertexBuffer, vertexDeclaration, verticesCount) },
                IndexBuffer   = new IndexBufferBinding(terrainIndexBuffer, false, indicesCount),
            };

            // Load the material and set parameters
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture0, WaterTexture);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture1, GrassTexture);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture2, MountainTexture);

            // Set up material regions
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight0, -10);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight0, 40);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight0, 70);

            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight1, 60);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight1, 80);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight1, 90);

            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight2, 85);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight2, 95);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight2, 125);

            terrainMesh = new Mesh {
                Draw = meshDraw, MaterialIndex = 0
            };
            TerrainEntity.GetOrCreate <ModelComponent>().Model = new Model {
                terrainMesh, TerrainMaterial
            };
        }
Exemple #14
0
        /// <summary>
        /// Creates an Entity that contains our dynamic Vertex and Index buffers.
        /// This Entity will be rendered by the model renderer.
        /// </summary>
        /// <param name="verticesCount"></param>
        /// <param name="indicesCount"></param>
        private void CreateTerrainModelEntity(int verticesCount, int indicesCount)
        {
            // Compute sizes
            var vertexDeclaration = VertexNormalTexture.VertexDeclaration;
            var vertexBufferSize = verticesCount * vertexDeclaration.CalculateSize();
            var indexBufferSize = indicesCount * sizeof(short);

            // Create Vertex and Index buffers
            terrainVertexBuffer = Buffer.Vertex.New(GraphicsDevice, vertexBufferSize, GraphicsResourceUsage.Dynamic);
            terrainIndexBuffer = Buffer.New(GraphicsDevice, indexBufferSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic);

            // Prepare mesh and entity
            var meshDraw = new MeshDraw
            {
                PrimitiveType = PrimitiveType.TriangleStrip,
                VertexBuffers = new[] { new VertexBufferBinding(terrainVertexBuffer, vertexDeclaration, verticesCount) },
                IndexBuffer = new IndexBufferBinding(terrainIndexBuffer, false, indicesCount),
            };

            // Load the material and set parameters
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture0, WaterTexture);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture1, GrassTexture);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MeshTexture2, MountainTexture);

            // Set up material regions
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight0, -10);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight0, 40);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight0, 70);

            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight1, 60);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight1, 80);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight1, 90);

            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MinimumHeight2, 85);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.OptimalHeight2, 95);
            TerrainMaterial.Parameters.Set(VertexTextureTerrainKeys.MaximumHeight2, 125);

            terrainMesh = new Mesh { Draw = meshDraw, MaterialIndex = 0 };
            TerrainEntity.GetOrCreate<ModelComponent>().Model = new Model { terrainMesh, TerrainMaterial };
        }
            public override unsafe void ApplyViewParameters(RenderDrawContext context, int viewIndex, ParameterCollection parameters)
            {
                // Note: no need to fill CurrentLights since we have no shadow maps
                base.ApplyViewParameters(context, viewIndex, parameters);

                var renderView = renderViews[viewIndex];

                var viewSize = renderView.ViewSize;

                // No screen size set?
                if (viewSize.X == 0 || viewSize.Y == 0)
                {
                    return;
                }

                var clusterCountX = ((int)viewSize.X + ClusterSize - 1) / ClusterSize;
                var clusterCountY = ((int)viewSize.Y + ClusterSize - 1) / ClusterSize;

                // TODO: Additional culling on x/y (to remove corner clusters)
                // Prepare planes for culling
                //var viewProjection = renderView.ViewProjection;
                //Array.Resize(ref zPlanes, ClusterSlices + 1);
                //for (int z = 0; z <= ClusterSlices; ++z)
                //{
                //    var zFactor = (float)z / (float)ClusterSlices;
                //
                //    // Build planes between nearplane and -farplane (see BoundingFrustum code)
                //    zPlanes[z] = new Plane(
                //        viewProjection.M13 - zFactor * viewProjection.M14,
                //        viewProjection.M23 - zFactor * viewProjection.M24,
                //        viewProjection.M33 - zFactor * viewProjection.M34,
                //        viewProjection.M43 - zFactor * viewProjection.M44);
                //
                //    zPlanes[z].Normalize();
                //}

                if (pointGroupRenderer.lightClusters == null || lightClustersValues.Length != clusterCountX * clusterCountY * ClusterSlices)
                {
                    // First time?
                    pointGroupRenderer.lightClusters?.Dispose();
                    pointGroupRenderer.lightClusters = Texture.New3D(context.GraphicsDevice, clusterCountX, clusterCountY, 8, PixelFormat.R32G32_UInt);
                    lightClustersValues = new Int2[clusterCountX * clusterCountY * ClusterSlices];
                }

                // Initialize cluster with no light (-1)
                for (int i = 0; i < clusterCountX * clusterCountY * ClusterSlices; ++i)
                {
                    lightNodes.Add(new LightClusterLinkedNode(LightType.Point, -1, -1));
                }

                // List of clusters moved by this light
                var movedClusters = new Dictionary <LightClusterLinkedNode, int>();

                // Try to use SpecialNearPlane to not waste too much slices in very small depth
                // Make sure we don't go to more than 10% of max depth
                var nearPlane = Math.Max(Math.Min(SpecialNearPlane, renderView.FarClipPlane * 0.1f), renderView.NearClipPlane);

                //var sliceBias = ((renderView.NearClipPlane * renderView.Projection.M33) + renderView.Projection.M43) / (renderView.NearClipPlane * renderView.Projection.M34);
                // Compute scale and bias so that near_plane..special_near fits in slice 0, then grow exponentionally
                //   log2(specialNear * scale + bias) == 1.0
                //   log2(far * scale + bias) == ClusterSlices
                // as a result:
                clusterDepthScale = (float)(Math.Pow(2.0f, ClusterSlices) - 2.0f) / (renderView.FarClipPlane - nearPlane);
                clusterDepthBias  = 2.0f - clusterDepthScale * nearPlane;

                //---------------- SPOT LIGHTS -------------------
                var lightRange = pointGroupRenderer.spotGroup.LightRanges[viewIndex];

                for (int i = lightRange.Start; i < lightRange.End; ++i)
                {
                    var light     = pointGroupRenderer.spotGroup.Lights[i].Light;
                    var spotLight = (LightSpot)light.Type;

                    if (spotLight.Shadow != null && spotLight.Shadow.Enabled)
                    {
                        continue;
                    }

                    // Create spot light data
                    var spotLightData = new SpotLightData
                    {
                        PositionWS  = light.Position,
                        DirectionWS = light.Direction,
                        AngleOffsetAndInvSquareRadius = new Vector3(spotLight.LightAngleScale, spotLight.LightAngleOffset, spotLight.InvSquareRange),
                        Color = light.Color,
                    };

                    // Fill list of spot lights
                    spotLights.Add(spotLightData);

                    movedClusters.Clear();

                    var radius = (float)Math.Sqrt(1.0f / spotLightData.AngleOffsetAndInvSquareRadius.Z);

                    Vector3 positionVS;
                    Vector3.TransformCoordinate(ref spotLightData.PositionWS, ref renderView.View, out positionVS);

                    // TODO: culling (first do it on PointLight, then backport it to SpotLight and improve for SpotLight case)
                    // Find x/y ranges
                    Vector2 clipMin, clipMax;
                    ComputeClipRegion(positionVS, radius, ref renderView.Projection, out clipMin, out clipMax);

                    var tileStartX = MathUtil.Clamp((int)((clipMin.X * 0.5f + 0.5f) * viewSize.X / ClusterSize), 0, clusterCountX);
                    var tileEndX   = MathUtil.Clamp((int)((clipMax.X * 0.5f + 0.5f) * viewSize.X / ClusterSize) + 1, 0, clusterCountX);
                    var tileStartY = MathUtil.Clamp((int)((-clipMax.Y * 0.5f + 0.5f) * viewSize.Y / ClusterSize), 0, clusterCountY);
                    var tileEndY   = MathUtil.Clamp((int)((-clipMin.Y * 0.5f + 0.5f) * viewSize.Y / ClusterSize) + 1, 0, clusterCountY);

                    // Find z range (project using Projection matrix)
                    var startZ = -positionVS.Z - radius;
                    var endZ   = -positionVS.Z + radius;

                    var tileStartZ = MathUtil.Clamp((int)Math.Log(startZ * clusterDepthScale + clusterDepthBias, 2.0f), 0, ClusterSlices);
                    var tileEndZ   = MathUtil.Clamp((int)Math.Log(endZ * clusterDepthScale + clusterDepthBias, 2.0f) + 1, 0, ClusterSlices);

                    for (int z = tileStartZ; z < tileEndZ; ++z)
                    {
                        for (int y = tileStartY; y < tileEndY; ++y)
                        {
                            for (int x = tileStartX; x < tileEndX; ++x)
                            {
                                AddLightToCluster(movedClusters, LightType.Spot, i - lightRange.Start, x + (y + z * clusterCountY) * clusterCountX);
                            }
                        }
                    }
                }

                //---------------- POINT LIGHTS -------------------
                lightRange = LightRanges[viewIndex];
                for (int i = lightRange.Start; i < lightRange.End; ++i)
                {
                    var light      = Lights[i].Light;
                    var pointLight = (LightPoint)light.Type;

                    // Create point light data
                    var pointLightData = new PointLightData
                    {
                        PositionWS      = light.Position,
                        InvSquareRadius = pointLight.InvSquareRadius,
                        Color           = light.Color,
                    };

                    // Fill list of point lights
                    pointLights.Add(pointLightData);

                    movedClusters.Clear();

                    var radius = (float)Math.Sqrt(1.0f / pointLightData.InvSquareRadius);

                    Vector3 positionVS;
                    Vector3.TransformCoordinate(ref pointLightData.PositionWS, ref renderView.View, out positionVS);

                    //Vector3 positionScreen;
                    //Vector3.TransformCoordinate(ref pointLightData.PositionWS, ref renderView.ViewProjection, out positionScreen);

                    // Find x/y ranges
                    Vector2 clipMin, clipMax;
                    ComputeClipRegion(positionVS, radius, ref renderView.Projection, out clipMin, out clipMax);

                    var tileStartX = MathUtil.Clamp((int)((clipMin.X * 0.5f + 0.5f) * viewSize.X / ClusterSize), 0, clusterCountX);
                    var tileEndX   = MathUtil.Clamp((int)((clipMax.X * 0.5f + 0.5f) * viewSize.X / ClusterSize) + 1, 0, clusterCountX);
                    var tileStartY = MathUtil.Clamp((int)((-clipMax.Y * 0.5f + 0.5f) * viewSize.Y / ClusterSize), 0, clusterCountY);
                    var tileEndY   = MathUtil.Clamp((int)((-clipMin.Y * 0.5f + 0.5f) * viewSize.Y / ClusterSize) + 1, 0, clusterCountY);

                    // Find z range (project using Projection matrix)
                    var startZ = -positionVS.Z - radius;
                    var endZ   = -positionVS.Z + radius;

                    //var centerZ = (int)(positionVS.Z * ClusterDepthScale + ClusterDepthBias);
                    var tileStartZ = MathUtil.Clamp((int)Math.Log(startZ * clusterDepthScale + clusterDepthBias, 2.0f), 0, ClusterSlices);
                    var tileEndZ   = MathUtil.Clamp((int)Math.Log(endZ * clusterDepthScale + clusterDepthBias, 2.0f) + 1, 0, ClusterSlices);

                    for (int z = tileStartZ; z < tileEndZ; ++z)
                    {
                        // TODO: Additional culling on x/y (to remove corner clusters)
                        // See "Practical Clustered Shading" for details
                        //if (z != centerZ)
                        //{
                        //    var plane = z < centerZ ? zPlanes[z + 1] : -zPlanes[z];
                        //
                        //    positionScreen = Plane.DotCoordinate(ref plane, ref positionScreen, out )
                        //}

                        for (int y = tileStartY; y < tileEndY; ++y)
                        {
                            for (int x = tileStartX; x < tileEndX; ++x)
                            {
                                AddLightToCluster(movedClusters, LightType.Point, i - lightRange.Start, x + (y + z * clusterCountY) * clusterCountX);
                            }
                        }
                    }
                }

                // Finish clusters by making their last element unique and building clusterInfos
                movedClusters.Clear();
                for (int i = 0; i < clusterCountX * clusterCountY * ClusterSlices; ++i)
                {
                    FinishCluster(movedClusters, i);
                }

                // Prepare light clusters
                for (int i = 0; i < clusterCountX * clusterCountY * ClusterSlices; ++i)
                {
                    var clusterId = lightNodes[i].NextNode;
                    lightClustersValues[i] = clusterId != -1 ? clusterInfos[clusterId] : new Int2(0, 0);
                }

                // Upload data to texture
                using (new DefaultCommandListLock(context.CommandList))
                {
                    fixed(Int2 *dataPtr = lightClustersValues)
                    context.CommandList.UpdateSubresource(pointGroupRenderer.lightClusters, 0, new DataBox((IntPtr)dataPtr, sizeof(Int2) * clusterCountX, sizeof(Int2) * clusterCountX * clusterCountY));

                    // PointLights: Ensure size and update
                    if (pointLights.Count > 0)
                    {
                        if (pointGroupRenderer.pointLightsBuffer == null || pointGroupRenderer.pointLightsBuffer.SizeInBytes < pointLights.Count * sizeof(PointLightData))
                        {
                            pointGroupRenderer.pointLightsBuffer?.Dispose();
                            pointGroupRenderer.pointLightsBuffer = Buffer.New(context.GraphicsDevice, MathUtil.NextPowerOfTwo(pointLights.Count * sizeof(PointLightData)), 0, BufferFlags.ShaderResource, PixelFormat.R32G32B32A32_Float);
                        }
                        fixed(PointLightData *pointLightsPtr = pointLights.Items)
                        context.CommandList.UpdateSubresource(pointGroupRenderer.pointLightsBuffer, 0, new DataBox((IntPtr)pointLightsPtr, 0, 0), new ResourceRegion(0, 0, 0, pointLights.Count * sizeof(PointLightData), 1, 1));
                    }
#if SILICONSTUDIO_PLATFORM_MACOS
                    // macOS doesn't like when we provide a null Buffer or if it is not sufficiently allocated.
                    // It would cause an inifite loop. So for now we just create one with one element but not initializing it.
                    else if (pointGroupRenderer.pointLightsBuffer == null || pointGroupRenderer.pointLightsBuffer.SizeInBytes < sizeof(PointLightData))
                    {
                        pointGroupRenderer.pointLightsBuffer?.Dispose();
                        pointGroupRenderer.pointLightsBuffer = Buffer.New(context.GraphicsDevice, MathUtil.NextPowerOfTwo(sizeof(PointLightData)), 0, BufferFlags.ShaderResource, PixelFormat.R32G32B32A32_Float);
                    }
#endif

                    // SpotLights: Ensure size and update
                    if (spotLights.Count > 0)
                    {
                        if (pointGroupRenderer.spotLightsBuffer == null || pointGroupRenderer.spotLightsBuffer.SizeInBytes < spotLights.Count * sizeof(SpotLightData))
                        {
                            pointGroupRenderer.spotLightsBuffer?.Dispose();
                            pointGroupRenderer.spotLightsBuffer = Buffer.New(context.GraphicsDevice, MathUtil.NextPowerOfTwo(spotLights.Count * sizeof(SpotLightData)), 0, BufferFlags.ShaderResource,
                                                                             PixelFormat.R32G32B32A32_Float);
                        }
                        fixed(SpotLightData *spotLightsPtr = spotLights.Items)
                        context.CommandList.UpdateSubresource(pointGroupRenderer.spotLightsBuffer, 0, new DataBox((IntPtr)spotLightsPtr, 0, 0),
                                                              new ResourceRegion(0, 0, 0, spotLights.Count * sizeof(SpotLightData), 1, 1));
                    }
#if SILICONSTUDIO_PLATFORM_MACOS
                    // See previous macOS comment.
                    else if (pointGroupRenderer.spotLightsBuffer == null || pointGroupRenderer.spotLightsBuffer.SizeInBytes < sizeof(SpotLightData))
                    {
                        pointGroupRenderer.spotLightsBuffer?.Dispose();
                        pointGroupRenderer.spotLightsBuffer = Buffer.New(context.GraphicsDevice, MathUtil.NextPowerOfTwo(sizeof(SpotLightData)), 0, BufferFlags.ShaderResource, PixelFormat.R32G32B32A32_Float);
                    }
#endif
                    // LightIndices: Ensure size and update
                    if (lightIndices.Count > 0)
                    {
                        if (pointGroupRenderer.lightIndicesBuffer == null || pointGroupRenderer.lightIndicesBuffer.SizeInBytes < lightIndices.Count * sizeof(int))
                        {
                            pointGroupRenderer.lightIndicesBuffer?.Dispose();
                            pointGroupRenderer.lightIndicesBuffer = Buffer.New(context.GraphicsDevice, MathUtil.NextPowerOfTwo(lightIndices.Count * sizeof(int)), 0, BufferFlags.ShaderResource,
                                                                               PixelFormat.R32_UInt);
                        }
                        fixed(int *lightIndicesPtr = lightIndices.Items)
                        context.CommandList.UpdateSubresource(pointGroupRenderer.lightIndicesBuffer, 0, new DataBox((IntPtr)lightIndicesPtr, 0, 0),
                                                              new ResourceRegion(0, 0, 0, lightIndices.Count * sizeof(int), 1, 1));
                    }
#if SILICONSTUDIO_PLATFORM_MACOS
                    // See previous macOS comment.
                    else if (pointGroupRenderer.lightIndicesBuffer == null || pointGroupRenderer.lightIndicesBuffer.SizeInBytes < sizeof(int))
                    {
                        pointGroupRenderer.lightIndicesBuffer?.Dispose();
                        pointGroupRenderer.lightIndicesBuffer = Buffer.New(context.GraphicsDevice, MathUtil.NextPowerOfTwo(sizeof(int)), 0, BufferFlags.ShaderResource, PixelFormat.R32_UInt);
                    }
#endif
                }

                // Clear data
                pointLights.Clear();
                spotLights.Clear();
                lightIndices.Clear();
                lightNodes.Clear();
                clusterInfos.Clear();

                // Set resources
                parameters.Set(LightClusteredPointGroupKeys.PointLights, pointGroupRenderer.pointLightsBuffer);
                parameters.Set(LightClusteredSpotGroupKeys.SpotLights, pointGroupRenderer.spotLightsBuffer);
                parameters.Set(LightClusteredKeys.LightIndices, pointGroupRenderer.lightIndicesBuffer);
                parameters.Set(LightClusteredKeys.LightClusters, pointGroupRenderer.lightClusters);

                parameters.Set(LightClusteredKeys.ClusterDepthScale, clusterDepthScale);
                parameters.Set(LightClusteredKeys.ClusterDepthBias, clusterDepthBias);
            }
Exemple #16
0
 /// <summary>
 /// Copies the content of this buffer from GPU memory to an array of data on CPU memory using a specific staging resource.
 /// </summary>
 /// <typeparam name="TData">The type of the T data.</typeparam>
 /// <param name="stagingTexture">The staging buffer used to transfer the buffer.</param>
 /// <param name="toData">To data.</param>
 /// <exception cref="System.ArgumentException">When strides is different from optimal strides, and TData is not the same size as the pixel format, or Width * Height != toData.Length</exception>
 /// <remarks>
 /// This method is only working when called from the main thread that is accessing the main <see cref="GraphicsDevice"/>.
 /// </remarks>
 public unsafe void GetData <TData>(CommandList commandList, Buffer stagingTexture, TData[] toData) where TData : struct
 {
     GetData(commandList, stagingTexture, new DataPointer(Interop.Fixed(toData), toData.Length * Utilities.SizeOf <TData>()));
 }
Exemple #17
0
            public void DisposeBuffers()
            {
                if (AppendBuffer != null)
                {
                    AppendBuffer.Release();
                    AppendBuffer = null;
                }

                if (ConsumeBuffer != null)
                {
                    ConsumeBuffer.Release();
                    ConsumeBuffer = null;
                }
            }
Exemple #18
0
 /// <summary>
 /// Allocates the buffers.
 /// </summary>
 public void AllocateBuffers(GraphicsDevice graphicsDevice)
 {
     DisposeBuffers();
     AppendBuffer = Buffer.StructuredAppend.New(graphicsDevice, UserState.Count, StructureSize);
     ConsumeBuffer = Buffer.StructuredAppend.New(graphicsDevice, UserState.Count, StructureSize);
 }
Exemple #19
0
 /// <summary>
 /// Allocates global buffers.
 /// </summary>
 private void OnCapacityCountChange()
 {
     // Allocate global buffers
     bool allocateGlobalBuffer = false;
     if (globalBuffer == null)
     {
         allocateGlobalBuffer = true;
     }
     else if (globalBuffer.ElementCount != CapacityCount)
     {
         // Reallocate global buffer if capacity changed
         allocateGlobalBuffer = true;
         // Release previous buffer
         globalBuffer.Release();
         sortBuffer.Release();
     }
     
     // Allocate new buffers
     if (allocateGlobalBuffer)
     {
         if (CapacityCount > 0)
         {
             globalBuffer = Buffer.Structured.New(graphicsDeviceService.GraphicsDevice, CapacityCount, StructureSize, true);
             globalBuffer.Name = "ParticleGlobalBuffer";
             sortBuffer = Buffer.Structured.New(graphicsDeviceService.GraphicsDevice, CapacityCount, sizeof(int) * 2, true);
             sortBuffer.Name = "ParticleSortBuffer";
         }
     }
 }
            public unsafe void AllocateBuffers(RenderDrawContext renderDrawContext, int vertexBufferSize, int requiredIndexCount)
            {
                // Build the shared vertex buffer - every frame
                if (vertexBufferSize > 0)
                {
                    {
                        vertexBufferSize--;
                        vertexBufferSize |= vertexBufferSize >> 1;
                        vertexBufferSize |= vertexBufferSize >> 2;
                        vertexBufferSize |= vertexBufferSize >> 3;
                        vertexBufferSize |= vertexBufferSize >> 8;
                        vertexBufferSize |= vertexBufferSize >> 16;
                        vertexBufferSize++;
                    }

                    VertexBufferSize = vertexBufferSize;

                    VertexBuffer = renderDrawContext.GraphicsContext.Allocator.GetTemporaryBuffer(
                        new BufferDescription(VertexBufferSize, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic));
                }

                // Build the shared index buffer - only when necessary
                var requiredIndexBufferSize = requiredIndexCount * IndexStride;
                if (requiredIndexBufferSize > IndexBufferSize)
                {
                    if (IndexBuffer != null)
                    {
                        renderDrawContext.GraphicsContext.Allocator.ReleaseReference(IndexBuffer);
                        IndexBuffer = null;
                    }

                    //  We start allocating from 64K (allowing 32K indices to be written at once - this is most probably going to be sufficient in all cases)
                    IndexBufferSize = requiredIndexBufferSize;
                    if (IndexBufferSize < 64 * 1024)
                        IndexBufferSize = 64 * 1024;

                    {
                        IndexBufferSize--;
                        IndexBufferSize |= IndexBufferSize >> 1;
                        IndexBufferSize |= IndexBufferSize >> 2;
                        IndexBufferSize |= IndexBufferSize >> 3;
                        IndexBufferSize |= IndexBufferSize >> 8;
                        IndexBufferSize |= IndexBufferSize >> 16;
                        IndexBufferSize++;
                    }

                    IndexBuffer = renderDrawContext.GraphicsContext.Allocator.GetTemporaryBuffer(
                        new BufferDescription(IndexBufferSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic));

                    var indexCount = IndexBufferSize / IndexStride;
                    indexCount = ((indexCount / 6) * 6);
                    {
                        var commandList = renderDrawContext.CommandList;

                        var mappedIndices = commandList.MapSubresource(IndexBuffer, 0, MapMode.WriteNoOverwrite, false, 0, IndexBufferSize);
                        var indexPointer = mappedIndices.DataBox.DataPointer;

                        int indexStructSize = sizeof(short);
                        int verticesPerQuad = 4;

                        var k = 0;
                        for (var i = 0; i < indexCount; k += verticesPerQuad)
                        {
                            *(short*)(indexPointer + indexStructSize * i++) = (short)(k + 0);
                            *(short*)(indexPointer + indexStructSize * i++) = (short)(k + 1);
                            *(short*)(indexPointer + indexStructSize * i++) = (short)(k + 2);
                            *(short*)(indexPointer + indexStructSize * i++) = (short)(k + 0);
                            *(short*)(indexPointer + indexStructSize * i++) = (short)(k + 2);
                            *(short*)(indexPointer + indexStructSize * i++) = (short)(k + 3);
                        }

                        commandList.UnmapSubresource(mappedIndices);
                    }
                }
            }
            public unsafe void AllocateBuffers(RenderDrawContext renderDrawContext, int vertexBufferSize, int requiredIndexCount)
            {
                // Build the shared vertex buffer - every frame
                if (vertexBufferSize > 0)
                {
                    {
                        vertexBufferSize--;
                        vertexBufferSize |= vertexBufferSize >> 1;
                        vertexBufferSize |= vertexBufferSize >> 2;
                        vertexBufferSize |= vertexBufferSize >> 3;
                        vertexBufferSize |= vertexBufferSize >> 8;
                        vertexBufferSize |= vertexBufferSize >> 16;
                        vertexBufferSize++;
                    }

                    VertexBufferSize = vertexBufferSize;

                    VertexBuffer = renderDrawContext.GraphicsContext.Allocator.GetTemporaryBuffer(
                        new BufferDescription(VertexBufferSize, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic));
                }

                // Build the shared index buffer - only when necessary
                var requiredIndexBufferSize = requiredIndexCount * IndexStride;

                if (requiredIndexBufferSize > IndexBufferSize)
                {
                    if (IndexBuffer != null)
                    {
                        renderDrawContext.GraphicsContext.Allocator.ReleaseReference(IndexBuffer);
                        IndexBuffer = null;
                    }

                    //  We start allocating from 64K (allowing 32K indices to be written at once - this is most probably going to be sufficient in all cases)
                    IndexBufferSize = requiredIndexBufferSize;
                    if (IndexBufferSize < 64 * 1024)
                    {
                        IndexBufferSize = 64 * 1024;
                    }

                    {
                        IndexBufferSize--;
                        IndexBufferSize |= IndexBufferSize >> 1;
                        IndexBufferSize |= IndexBufferSize >> 2;
                        IndexBufferSize |= IndexBufferSize >> 3;
                        IndexBufferSize |= IndexBufferSize >> 8;
                        IndexBufferSize |= IndexBufferSize >> 16;
                        IndexBufferSize++;
                    }

                    IndexBuffer = renderDrawContext.GraphicsContext.Allocator.GetTemporaryBuffer(
                        new BufferDescription(IndexBufferSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic));

                    var indexCount = IndexBufferSize / IndexStride;
                    indexCount = ((indexCount / 6) * 6);
                    {
                        var commandList = renderDrawContext.CommandList;

                        var mappedIndices = commandList.MapSubresource(IndexBuffer, 0, MapMode.WriteNoOverwrite, false, 0, IndexBufferSize);
                        var indexPointer  = mappedIndices.DataBox.DataPointer;

                        int indexStructSize = sizeof(short);
                        int verticesPerQuad = 4;

                        var k = 0;
                        for (var i = 0; i < indexCount; k += verticesPerQuad)
                        {
                            *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 0);
                            *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 1);
                            *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 2);
                            *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 0);
                            *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 2);
                            *(short *)(indexPointer + indexStructSize * i++) = (short)(k + 3);
                        }

                        commandList.UnmapSubresource(mappedIndices);
                    }
                }
            }
 public void ReleaseBuffers(RenderDrawContext renderDrawContext)
 {
     // Release the temporary vertex buffer
     if (VertexBuffer != null)
     {
         renderDrawContext.GraphicsContext.Allocator.ReleaseReference(VertexBuffer);
         VertexBuffer = null;
     }
 }
Exemple #23
0
 public void Dispose()
 {
     if (GeneratedIndicesAEN != null)
     {
         GeneratedIndicesAEN.Dispose();
         GeneratedIndicesAEN = null;
     }
 }
        public void Generate(IServiceRegistry services, Model model)
        {
            if (model == null)
            {
                throw new ArgumentNullException(nameof(model));
            }

            var needsTempDevice = false;
            var graphicsDevice  = services?.GetSafeServiceAs <IGraphicsDeviceService>().GraphicsDevice;

            if (graphicsDevice == null)
            {
                graphicsDevice  = GraphicsDevice.New();
                needsTempDevice = true;
            }

            var data = CreatePrimitiveMeshData();

            if (data.Vertices.Length == 0)
            {
                throw new InvalidOperationException("Invalid GeometricPrimitive [{0}]. Expecting non-zero Vertices array");
            }

            if (LocalOffset != Vector3.Zero)
            {
                for (var index = 0; index < data.Vertices.Length; index++)
                {
                    data.Vertices[index].Position += LocalOffset;
                }
            }

            //Scale if necessary
            if (Scale != Vector3.One)
            {
                var inverseMatrix = Matrix.Scaling(Scale);
                inverseMatrix.Invert();
                for (var index = 0; index < data.Vertices.Length; index++)
                {
                    data.Vertices[index].Position *= Scale;
                    Vector3.TransformCoordinate(ref data.Vertices[index].Normal, ref inverseMatrix, out data.Vertices[index].Normal);
                }
            }

            var boundingBox = BoundingBox.Empty;

            for (int i = 0; i < data.Vertices.Length; i++)
            {
                BoundingBox.Merge(ref boundingBox, ref data.Vertices[i].Position, out boundingBox);
            }

            BoundingSphere boundingSphere;

            unsafe
            {
                fixed(void *verticesPtr = data.Vertices)
                BoundingSphere.FromPoints((IntPtr)verticesPtr, 0, data.Vertices.Length, VertexPositionNormalTexture.Size, out boundingSphere);
            }

            var originalLayout = data.Vertices[0].GetLayout();

            // Generate Tangent/BiNormal vectors
            var resultWithTangentBiNormal = VertexHelper.GenerateTangentBinormal(originalLayout, data.Vertices, data.Indices);

            // Generate Multitexcoords
            var result = VertexHelper.GenerateMultiTextureCoordinates(resultWithTangentBiNormal);

            var meshDraw = new MeshDraw();

            var layout       = result.Layout;
            var vertexBuffer = result.VertexBuffer;
            var indices      = data.Indices;

            if (indices.Length < 0xFFFF)
            {
                var indicesShort = new ushort[indices.Length];
                for (int i = 0; i < indicesShort.Length; i++)
                {
                    indicesShort[i] = (ushort)indices[i];
                }
                meshDraw.IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indicesShort).RecreateWith(indicesShort), false, indices.Length);
                if (needsTempDevice)
                {
                    var indexData = BufferData.New(BufferFlags.IndexBuffer, indicesShort);
                    meshDraw.IndexBuffer = new IndexBufferBinding(indexData.ToSerializableVersion(), false, indices.Length);
                }
            }
            else
            {
                if (graphicsDevice.Features.CurrentProfile <= GraphicsProfile.Level_9_3)
                {
                    throw new InvalidOperationException("Cannot generate more than 65535 indices on feature level HW <= 9.3");
                }

                meshDraw.IndexBuffer = new IndexBufferBinding(Buffer.Index.New(graphicsDevice, indices).RecreateWith(indices), true, indices.Length);
                if (needsTempDevice)
                {
                    var indexData = BufferData.New(BufferFlags.IndexBuffer, indices);
                    meshDraw.IndexBuffer = new IndexBufferBinding(indexData.ToSerializableVersion(), true, indices.Length);
                }
            }

            meshDraw.VertexBuffers = new[] { new VertexBufferBinding(Buffer.New(graphicsDevice, vertexBuffer, BufferFlags.VertexBuffer).RecreateWith(vertexBuffer), layout, data.Vertices.Length) };
            if (needsTempDevice)
            {
                var vertexData = BufferData.New(BufferFlags.VertexBuffer, vertexBuffer);
                meshDraw.VertexBuffers = new[] { new VertexBufferBinding(vertexData.ToSerializableVersion(), layout, data.Vertices.Length) };
            }

            meshDraw.DrawCount     = indices.Length;
            meshDraw.PrimitiveType = PrimitiveType.TriangleList;

            var mesh = new Mesh {
                Draw = meshDraw, BoundingBox = boundingBox, BoundingSphere = boundingSphere
            };

            model.BoundingBox    = boundingBox;
            model.BoundingSphere = boundingSphere;
            model.Add(mesh);

            if (MaterialInstance?.Material != null)
            {
                model.Materials.Add(MaterialInstance);
            }

            if (needsTempDevice)
            {
                graphicsDevice.Dispose();
            }
        }
Exemple #25
0
        private static byte[] GetDataSafe(this Buffer buffer)
        {
            var data = buffer.GetSerializationData();

            return(data != null ? data.Content : buffer.GetData <byte>());
        }
 /// <summary>
 /// Allocates the buffers.
 /// </summary>
 public void AllocateBuffers(GraphicsDevice graphicsDevice)
 {
     DisposeBuffers();
     AppendBuffer  = Buffer.StructuredAppend.New(graphicsDevice, UserState.Count, StructureSize);
     ConsumeBuffer = Buffer.StructuredAppend.New(graphicsDevice, UserState.Count, StructureSize);
 }
Exemple #27
0
        protected override void DrawCore(RenderDrawContext context)
        {
            var inputTexture = RadianceMap;

            if (inputTexture == null)
            {
                return;
            }


            const int FirstPassBlockSize = 4;
            const int FirstPassSumsCount = FirstPassBlockSize * FirstPassBlockSize;

            var faceCount = inputTexture.Dimension == TextureDimension.TextureCube ? 6 : 1;

            if (faceCount == 1)
            {
                throw new NotSupportedException("Only texture cube are currently supported as input of 'LambertianPrefilteringSH' effect.");
            }
            var inputSize         = new Int2(inputTexture.Width, inputTexture.Height); // (Note: for cube maps width = height)
            var coefficientsCount = harmonicalOrder * harmonicalOrder;

            var sumsToPerfomRemaining = inputSize.X * inputSize.Y * faceCount / FirstPassSumsCount;
            var partialSumBuffer      = NewScopedTypedBuffer(coefficientsCount * sumsToPerfomRemaining, PixelFormat.R32G32B32A32_Float, true);


            // Project the radiance on the SH basis and sum up the results along the 4x4 blocks
            firstPassEffect.ThreadNumbers     = new Int3(FirstPassBlockSize, FirstPassBlockSize, 1);
            firstPassEffect.ThreadGroupCounts = new Int3(inputSize.X / FirstPassBlockSize, inputSize.Y / FirstPassBlockSize, faceCount);
            firstPassEffect.Parameters.Set(LambertianPrefilteringSHParameters.BlockSize, FirstPassBlockSize);
            firstPassEffect.Parameters.Set(SphericalHarmonicsParameters.HarmonicsOrder, harmonicalOrder);
            firstPassEffect.Parameters.Set(LambertianPrefilteringSHPass1Keys.RadianceMap, inputTexture);
            firstPassEffect.Parameters.Set(LambertianPrefilteringSHPass1Keys.OutputBuffer, partialSumBuffer);
            ((RendererBase)firstPassEffect).Draw(context);

            // Recursively applies the pass2 (sums the coefficients together) as long as needed. Swap input/output buffer at each iteration.
            var    secondPassInputBuffer  = partialSumBuffer;
            Buffer secondPassOutputBuffer = null;

            while (sumsToPerfomRemaining % 2 == 0)
            {
                // we are limited in the number of summing threads by the group-shared memory size.
                // determine the number of threads to use and update the number of sums remaining afterward.
                var sumsCount = 1;
                while (sumsCount < (1 << 10) && sumsToPerfomRemaining % 2 == 0) // shader can perform only an 2^x number of sums.
                {
                    sumsCount             <<= 1;
                    sumsToPerfomRemaining >>= 1;
                }

                // determine the numbers of groups (limited to 65535 groups by dimensions)
                var groupCountX = 1;
                var groupCountY = sumsToPerfomRemaining;
                while (groupCountX >= short.MaxValue)
                {
                    groupCountX <<= 1;
                    groupCountY >>= 1;
                }

                // create the output buffer if not existing yet
                if (secondPassOutputBuffer == null)
                {
                    secondPassOutputBuffer = NewScopedTypedBuffer(coefficientsCount * sumsToPerfomRemaining, PixelFormat.R32G32B32A32_Float, true);
                }

                // Unset outputs on D3D11
                // TODO: This should be done automatically on SetPipelineState
                context.CommandList.UnsetReadWriteBuffers();

                // draw pass 2
                secondPassEffect.ThreadNumbers     = new Int3(sumsCount, 1, 1);
                secondPassEffect.ThreadGroupCounts = new Int3(groupCountX, groupCountY, coefficientsCount);
                secondPassEffect.Parameters.Set(LambertianPrefilteringSHParameters.BlockSize, sumsCount);
                secondPassEffect.Parameters.Set(SphericalHarmonicsParameters.HarmonicsOrder, harmonicalOrder);
                secondPassEffect.Parameters.Set(LambertianPrefilteringSHPass2Keys.InputBuffer, secondPassInputBuffer);
                secondPassEffect.Parameters.Set(LambertianPrefilteringSHPass2Keys.OutputBuffer, secondPassOutputBuffer);
                ((RendererBase)secondPassEffect).Draw(context);

                // swap second pass input/output buffers.
                var swapTemp = secondPassOutputBuffer;
                secondPassOutputBuffer = secondPassInputBuffer;
                secondPassInputBuffer  = swapTemp;
            }

            // create and initialize result SH
            prefilteredLambertianSH = new SphericalHarmonics(HarmonicOrder);

            // Get the data out of the final buffer
            var sizeResult   = coefficientsCount * sumsToPerfomRemaining * PixelFormat.R32G32B32A32_Float.SizeInBytes();
            var stagedBuffer = NewScopedBuffer(new BufferDescription(sizeResult, BufferFlags.None, GraphicsResourceUsage.Staging));

            context.CommandList.CopyRegion(secondPassInputBuffer, 0, new ResourceRegion(0, 0, 0, sizeResult, 1, 1), stagedBuffer, 0);
            var finalsValues = stagedBuffer.GetData <Vector4>(context.CommandList);

            // performs last possible additions, normalize the result and store it in the SH
            for (var c = 0; c < coefficientsCount; c++)
            {
                var coeff = Vector4.Zero;
                for (var f = 0; f < sumsToPerfomRemaining; ++f)
                {
                    coeff += finalsValues[coefficientsCount * f + c];
                }
                prefilteredLambertianSH.Coefficients[c] = 4 * MathUtil.Pi / coeff.W * new Color3(coeff.X, coeff.Y, coeff.Z);
            }
        }
Exemple #28
0
 /// <summary>
 /// Sets a constant buffer view descriptor.
 /// </summary>
 /// <param name="slot">The slot.</param>
 /// <param name="buffer">The constant buffer.</param>
 /// <param name="offset">The constant buffer view start offset.</param>
 /// <param name="size">The constant buffer view size.</param>
 public void SetConstantBuffer(int slot, Buffer buffer, int offset, int size)
 {
     HeapObjects[DescriptorStartOffset + slot] = new DescriptorSetEntry(buffer, offset, size);
 }
        /// <summary>
        /// Bake lightprobes into buffers compatible with <see cref="LightProbeRenderer"/>
        /// </summary>
        /// <param name="drawContext">The drawing context</param>
        private unsafe void BakeLightProbes(RenderContext context, RenderDrawContext drawContext)
        {
            Texture ibl = null;
            Buffer  tetrahedronProbeIndices = null;
            Buffer  tetrahedronMatrices     = null;
            Buffer  lightprobesCoefficients = null;
            var     renderView = context.RenderView;

            var lightProbesData = renderView.SceneInstance.GetProcessor <LightProbeProcessor>()?.RuntimeData;

            if (lightProbesData == null || lightProbesData.Tetrahedra.Count == 0)
            {
                // No lightprobes, we still set GPU resources (otherwise rendering might fetch invalid data)
                goto SetGPUResources;
            }

            // First time initialization
            if (bakeLightProbes == null)
            {
                bakeLightProbes = new DynamicEffectInstance("XenkoBakeLightProbeEffect");
                bakeLightProbes.Initialize(Services);

                bakeLightProbesPipeline = new MutablePipelineState(GraphicsDevice);
                bakeLightProbesPipeline.State.InputElements = LightProbeVertex.Layout.CreateInputElements();
                bakeLightProbesPipeline.State.PrimitiveType = PrimitiveType.TriangleList;
            }

            // Render IBL tetrahedra ID so that we can assign them per pixel
            //ibl = PushScopedResource(Context.Allocator.GetTemporaryTexture2D(drawContext.CommandList.DepthStencilBuffer.Width, drawContext.CommandList.DepthStencilBuffer.Height, PixelFormat.R16_UInt));
            ibl = PushScopedResource(Context.Allocator.GetTemporaryTexture2D(TextureDescription.New2D(drawContext.CommandList.DepthStencilBuffer.Width, drawContext.CommandList.DepthStencilBuffer.Height,
                                                                                                      1, PixelFormat.R16_UInt, TextureFlags.ShaderResource | TextureFlags.RenderTarget, 1, GraphicsResourceUsage.Default, actualMultisampleCount)));
            using (drawContext.PushRenderTargetsAndRestore())
            {
                drawContext.CommandList.Clear(ibl, Color4.Black);
                drawContext.CommandList.SetRenderTarget(drawContext.CommandList.DepthStencilBuffer, ibl);

                bakeLightProbes.UpdateEffect(GraphicsDevice);

                bakeLightProbesPipeline.State.RootSignature  = bakeLightProbes.RootSignature;
                bakeLightProbesPipeline.State.EffectBytecode = bakeLightProbes.Effect.Bytecode;
                bakeLightProbesPipeline.State.RasterizerState.DepthClipEnable = false;
                bakeLightProbesPipeline.State.DepthStencilState = new DepthStencilStateDescription(true, false)
                {
                    StencilEnable = true,
                    FrontFace     = new DepthStencilStencilOpDescription
                    {
                        StencilDepthBufferFail = StencilOperation.Keep,
                        StencilFail            = StencilOperation.Keep,
                        StencilPass            = StencilOperation.Increment,
                        StencilFunction        = CompareFunction.Equal,
                    },
                };
                //bakeLightProbesPipeline.State.RasterizerState.DepthClipEnable = false;
                bakeLightProbesPipeline.State.Output.CaptureState(drawContext.CommandList);
                bakeLightProbesPipeline.Update();

                drawContext.CommandList.SetPipelineState(bakeLightProbesPipeline.CurrentState);
                drawContext.CommandList.SetStencilReference(0);

                // Apply the effect
                bakeLightProbes.Parameters.Set(BakeLightProbeShaderKeys.MatrixTransform, renderView.ViewProjection);
                bakeLightProbes.Apply(drawContext.GraphicsContext);

                /*int tetrahedrawGridSize = 5;
                 * Vector3 tetrahedraMin = new Vector3(-12.0f);
                 * Vector3 tetrahedraMax = new Vector3(12.0f);
                 * var lightprobePositions = new Vector3[tetrahedrawGridSize*tetrahedrawGridSize*tetrahedrawGridSize];
                 *
                 * for (int i = 0; i < lightprobePositions.Length; ++i)
                 * {
                 *  lightprobePositions[i] = new Vector3(
                 *      MathUtil.Lerp(tetrahedraMin.X, tetrahedraMax.X, (float)(i/(tetrahedrawGridSize*tetrahedrawGridSize))/(tetrahedrawGridSize - 1)),
                 *      MathUtil.Lerp(tetrahedraMin.Y, tetrahedraMax.Y, (float)((i/tetrahedrawGridSize)%tetrahedrawGridSize)/(tetrahedrawGridSize - 1)),
                 *      MathUtil.Lerp(tetrahedraMin.Z, tetrahedraMax.Z, (float)(i%tetrahedrawGridSize)/(tetrahedrawGridSize - 1)));
                 * }
                 *
                 * var tetra = new BowyerWatsonTetrahedralization();
                 * var tetraResult = tetra.Compute(lightprobePositions);*/

                Matrix viewInverse = renderView.View;
                viewInverse.Invert();

                var eye = new Vector3(viewInverse.M41, viewInverse.M42, viewInverse.M43);

                var tetraResult         = lightProbesData.Tetrahedra;
                var lightprobePositions = lightProbesData.Vertices;
                var lightprobeFaces     = lightProbesData.Faces;

                // We build a graph of tetrahedron connectivity from back to front, then do a topological sort on top of it
                var tetraDepth          = new TetrahedronSortKey[tetraResult.Count];
                var faceDirection       = new bool[lightprobeFaces.Count];
                var incomingEdges       = new byte[tetraResult.Count];
                var processQueue        = new Queue <int>();
                int processedTetrahedra = 0;

                for (int i = 0; i < lightprobeFaces.Count; ++i)
                {
                    var face = lightprobeFaces[i];

                    // Compute face orientations
                    var vertex0 = lightprobePositions[face.Vertices[0]];
                    Vector3.Subtract(ref vertex0, ref eye, out vertex0);
                    bool faceFrontFacing = Vector3.Dot(face.Normal, vertex0) >= 0.0f;
                    faceDirection[i] = faceFrontFacing;

                    // Only process edges that connect two tetrahedra (ignore boundaries for now)
                    if (face.BackTetrahedron != -1)
                    {
                        // Build list of incoming edges (back to front)
                        if (faceFrontFacing)
                        {
                            incomingEdges[face.FrontTetrahedron] |= (byte)(1 << face.FrontFace);
                        }
                        else
                        {
                            incomingEdges[face.BackTetrahedron] |= (byte)(1 << face.BackFace);
                        }
                    }
                }

                for (int i = 0; i < tetraResult.Count; ++i)
                {
                    // Tetrahedron without any incoming edges means they should be drawn first (graph nodes with no incoming edges for our topological sort)
                    if (incomingEdges[i] == 0)
                    {
                        processQueue.Enqueue(i);
                    }
                }

                // Perform topological sort
                while (processQueue.Count > 0)
                {
                    var tetrahedronIndex = processQueue.Dequeue();
                    tetraDepth[tetrahedronIndex] = new TetrahedronSortKey(tetrahedronIndex, processedTetrahedra++);
                    var tetrahedron = tetraResult[tetrahedronIndex];
                    //var frontFacingFaces = frontFacing[tetrahedronIndex];

                    // Process each outgoing face (edges in the graph)
                    for (int tetrahedronFace = 0; tetrahedronFace < 4; ++tetrahedronFace)
                    {
                        // Check if there is a neighbour
                        if (tetrahedron.Neighbours[tetrahedronFace] == -1)
                        {
                            continue;
                        }

                        var faceIndex     = tetrahedron.Faces[tetrahedronFace];
                        var realFaceIndex = faceIndex >= 0 ? faceIndex : ~faceIndex;

                        // Only process faces going back to front (outgoing edges)
                        if (faceDirection[realFaceIndex] == faceIndex >= 0)
                        {
                            continue;
                        }

                        var face = lightprobeFaces[realFaceIndex];

                        int   tetrahedronNeighbourIndex;
                        sbyte tetrahedronNeighbourFace;
                        if (faceIndex >= 0)
                        {
                            tetrahedronNeighbourIndex = face.BackTetrahedron;
                            tetrahedronNeighbourFace  = face.BackFace;
                        }
                        else
                        {
                            tetrahedronNeighbourIndex = face.FrontTetrahedron;
                            tetrahedronNeighbourFace  = face.FrontFace;
                        }

                        var neighbourTraversedFaces    = incomingEdges[tetrahedronNeighbourIndex];
                        var newNeighbourTraversedFaces = (byte)(neighbourTraversedFaces & ~(1 << tetrahedronNeighbourFace));

                        // Proceed only if something changed
                        if (newNeighbourTraversedFaces != neighbourTraversedFaces)
                        {
                            incomingEdges[tetrahedronNeighbourIndex] = newNeighbourTraversedFaces;
                            if (newNeighbourTraversedFaces == 0) // are all incoming edges already marked? If yes, go on
                            {
                                processQueue.Enqueue(tetrahedronNeighbourIndex);
                            }
                        }
                    }
                }

                Array.Sort(tetraDepth);


                // Draw shape
                tetrahedronMatrices     = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(tetraResult.Count * 3 * sizeof(Vector4), BufferFlags.ShaderResource, GraphicsResourceUsage.Default), PixelFormat.R32G32B32A32_Float));
                tetrahedronProbeIndices = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(tetraResult.Count * 4 * sizeof(int), BufferFlags.ShaderResource, GraphicsResourceUsage.Default), PixelFormat.R32G32B32A32_UInt));
                lightprobesCoefficients = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(lightProbesData.Coefficients.Length * sizeof(Color3), BufferFlags.ShaderResource, GraphicsResourceUsage.Default), PixelFormat.R32G32B32_Float));

                var tetraInsideIndex = -1;

                fixed(Color3 *lightProbeCoefficients = lightProbesData.Coefficients)
                fixed(Vector4 * matrices  = lightProbesData.Matrices)
                fixed(Int4 * probeIndices = lightProbesData.LightProbeIndices)
                {
                    drawContext.CommandList.UpdateSubresource(lightprobesCoefficients, 0, new DataBox((IntPtr)lightProbeCoefficients, 0, 0));
                    drawContext.CommandList.UpdateSubresource(tetrahedronProbeIndices, 0, new DataBox((IntPtr)probeIndices, 0, 0));
                    drawContext.CommandList.UpdateSubresource(tetrahedronMatrices, 0, new DataBox((IntPtr)matrices, 0, 0));

                    // Find which probe we are currently in
                    // TODO: Optimize (use previous coherency info?)
                    for (int i = 0; i < tetraResult.Count; ++i)
                    {
                        // Get tetrahedra matrix
                        var tetrahedraMatrix = Matrix.Identity;
                        tetrahedraMatrix.Column1 = matrices[i * 3 + 0];
                        tetrahedraMatrix.Column2 = matrices[i * 3 + 1];
                        tetrahedraMatrix.Column3 = matrices[i * 3 + 2];

                        // Extract and zero-out position of 3rd vertex (we get the 3x3 matrix)
                        var vertex3 = tetrahedraMatrix.TranslationVector;
                        tetrahedraMatrix.TranslationVector = Vector3.Zero;

                        Vector3 tetraFactors = Vector3.TransformCoordinate(eye - vertex3, tetrahedraMatrix);
                        var     tetraFactorW = 1.0f - tetraFactors.X - tetraFactors.Y - tetraFactors.Z;
                        if (tetraFactors.X >= 0.0f && tetraFactors.X <= 1.0f &&
                            tetraFactors.Y >= 0.0f && tetraFactors.Y <= 1.0f &&
                            tetraFactors.Z >= 0.0f && tetraFactors.Z <= 1.0f &&
                            tetraFactorW >= 0.0f && tetraFactorW <= 1.0f)
                        {
                            tetraInsideIndex = i;
                            break;
                        }
                    }
                }

                // Fill vertex/index buffers
                var vertexBuffer = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription((tetraResult.Count * 4 + 3) * LightProbeVertex.Size, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic)));
                var indexBuffer  = PushScopedResource(Context.Allocator.GetTemporaryBuffer(new BufferDescription(tetraResult.Count * 12 * sizeof(uint), BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic)));

                var mappedVertexBuffer = drawContext.CommandList.MapSubresource(vertexBuffer, 0, MapMode.WriteDiscard);
                var vertices           = (LightProbeVertex *)mappedVertexBuffer.DataBox.DataPointer;
                // Upload sorted tetrahedron indices
                for (int i = 0; i < tetraResult.Count; ++i)
                {
                    var sortedIndex = tetraDepth[i].Index;
                    var tetrahedra  = tetraResult[sortedIndex];
                    for (int j = 0; j < 4; ++j)
                    {
                        vertices[i * 4 + j] = new LightProbeVertex(lightprobePositions[tetrahedra.Vertices[j]], (uint)sortedIndex);
                    }
                }
                // Full screen pass
                if (tetraInsideIndex != -1)
                {
                    vertices[tetraResult.Count * 4 + 0] = new LightProbeVertex(new Vector3(-1, 1, 0), (uint)tetraInsideIndex);
                    vertices[tetraResult.Count * 4 + 1] = new LightProbeVertex(new Vector3(3, 1, 0), (uint)tetraInsideIndex);
                    vertices[tetraResult.Count * 4 + 2] = new LightProbeVertex(new Vector3(-1, -3, 0), (uint)tetraInsideIndex);
                }
                drawContext.CommandList.UnmapSubresource(mappedVertexBuffer);

                var mappedIndexBuffer = drawContext.CommandList.MapSubresource(indexBuffer, 0, MapMode.WriteDiscard);
                var indices           = (int *)mappedIndexBuffer.DataBox.DataPointer;
                for (int i = 0; i < tetraResult.Count; ++i)
                {
                    indices[i * 12 + 0] = i * 4 + 0;
                    indices[i * 12 + 1] = i * 4 + 2;
                    indices[i * 12 + 2] = i * 4 + 1;

                    indices[i * 12 + 3] = i * 4 + 1;
                    indices[i * 12 + 4] = i * 4 + 2;
                    indices[i * 12 + 5] = i * 4 + 3;

                    indices[i * 12 + 6] = i * 4 + 3;
                    indices[i * 12 + 7] = i * 4 + 2;
                    indices[i * 12 + 8] = i * 4 + 0;

                    indices[i * 12 + 9]  = i * 4 + 3;
                    indices[i * 12 + 10] = i * 4 + 0;
                    indices[i * 12 + 11] = i * 4 + 1;
                }
                drawContext.CommandList.UnmapSubresource(mappedIndexBuffer);

                drawContext.CommandList.SetVertexBuffer(0, vertexBuffer, 0, LightProbeVertex.Size);
                drawContext.CommandList.SetIndexBuffer(indexBuffer, 0, true);

                // Draw until current tetrahedra
                drawContext.CommandList.DrawIndexed(tetraResult.Count * 12);

                // For now, drawing them one by one (easier to debug)
                //for (int i = 0; i < tetraResult.Count; ++i)
                //{
                //    context.CommandList.DrawIndexed(12, i * 12);
                //}

                // Draw current tetrahedron we are in as full screen (fill stencil holes)
                if (tetraInsideIndex != -1)
                {
                    bakeLightProbesPipeline.State.DepthStencilState.DepthBufferEnable = false;
                    bakeLightProbesPipeline.Update();

                    drawContext.CommandList.SetPipelineState(bakeLightProbesPipeline.CurrentState);

                    // Apply the effect
                    bakeLightProbes.Parameters.Set(BakeLightProbeShaderKeys.MatrixTransform, Matrix.Identity);
                    bakeLightProbes.Apply(drawContext.GraphicsContext);

                    drawContext.CommandList.Draw(3, tetraResult.Count * 4);
                }

                // TODO: Draw the tetrahedron we are in full screen
                // context.CommandList.Draw...
            }

            // Set LightProbes resources
SetGPUResources:
            foreach (var renderFeature in context.RenderSystem.RenderFeatures)
            {
                if (!(renderFeature is RootEffectRenderFeature))
                {
                    continue;
                }

                var logicalKey  = ((RootEffectRenderFeature)renderFeature).CreateViewLogicalGroup("LightProbes");
                var viewFeature = renderView.Features[renderFeature.Index];

                foreach (var viewLayout in viewFeature.Layouts)
                {
                    var resourceGroup = viewLayout.Entries[renderView.Index].Resources;

                    var logicalGroup = viewLayout.GetLogicalGroup(logicalKey);
                    if (logicalGroup.Hash == ObjectId.Empty)
                    {
                        continue;
                    }

                    resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart, ibl);
                    resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart + 1, tetrahedronProbeIndices);
                    resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart + 2, tetrahedronMatrices);
                    resourceGroup.DescriptorSet.SetShaderResourceView(logicalGroup.DescriptorSlotStart + 3, lightprobesCoefficients);
                }
            }
        }