/// <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 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; } }
/// <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; } }
/// <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]); }
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; }
/// <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 }; }
/// <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); }
/// <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>())); }
/// <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); }
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 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(); } }
private static byte[] GetDataSafe(this Buffer buffer) { var data = buffer.GetSerializationData(); return(data != null ? data.Content : buffer.GetData <byte>()); }
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); } }
/// <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); } } }