diff --git a/sources/engine/Stride.Graphics/BatchBase.cs b/sources/engine/Stride.Graphics/BatchBase.cs index 5c680e8f99..0a39fcfa77 100644 --- a/sources/engine/Stride.Graphics/BatchBase.cs +++ b/sources/engine/Stride.Graphics/BatchBase.cs @@ -91,6 +91,7 @@ public ElementInfo(int vertexCount, int indexCount, in TDrawInfo drawInfo, float private ElementInfo[] drawsQueue; private int drawsQueueCount; private Texture[] drawTextures; + private List lastFrameBuffers; private readonly int vertexStructSize; private readonly int indexStructSize; @@ -141,6 +142,8 @@ protected BatchBase(GraphicsDevice device, EffectBytecode defaultEffectByteCode, // Creates the vertex buffer (shared by within a device context). // TODO: find a better way to do that, and check resource disposal ResourceContextPool = graphicsDevice.GetOrCreateSharedData(resourceBufferInfo.ResourceKey, d => new ThreadLocal(() => new DeviceResourceContext(graphicsDevice, vertexDeclaration, resourceBufferInfo), true)); + + lastFrameBuffers = new(); } protected override void Destroy() @@ -172,6 +175,10 @@ protected void Begin(GraphicsContext graphicsContext, EffectInstance effect, Spr { CheckEndHasBeenCalled("begin"); + foreach (var buffer in lastFrameBuffers) + GraphicsContext.Allocator.ReleaseReference(buffer); + lastFrameBuffers.Clear(); + ResourceContext = ResourceContextPool.Value; GraphicsContext = graphicsContext; @@ -418,120 +425,70 @@ private void DrawBatchPerTexture(Texture texture, ElementInfo[] sprites, int off private void DrawBatchPerTextureAndPass(ElementInfo[] sprites, int offset, int count) { - while (count > 0) + ResourceContext.VertexBufferPosition = ResourceContext.VertexCount; + ResourceContext.IndexBufferPosition = ResourceContext.IndexCount; + for (int end = offset + count; offset < end;) { - // How many index/vertex do we want to draw? - var indexCount = 0; - var vertexCount = 0; - var batchSize = 0; - - while (batchSize < count) + if (ResourceContext.VertexBufferPosition == ResourceContext.VertexCount || ResourceContext.IndexBufferPosition == ResourceContext.IndexCount) // Buffer is full, fetch the next ones { - var spriteIndex = offset + batchSize; - ref var spriteElementInfo = ref sprites[spriteIndex]; + ResourceContext.VertexBufferPosition = 0; + ResourceContext.IndexBufferPosition = 0; - // How many sprites does the D3D vertex buffer have room for? - var remainingVertexSpace = ResourceContext.VertexCount - ResourceContext.VertexBufferPosition - vertexCount; - var remainingIndexSpace = ResourceContext.IndexCount - ResourceContext.IndexBufferPosition - indexCount; + ResourceContext.VertexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.VertexCount * vertexStructSize, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic)); + GraphicsContext.CommandList.SetVertexBuffer(0, ResourceContext.VertexBuffer, 0, vertexStructSize); + lastFrameBuffers.Add(ResourceContext.VertexBuffer); - // if there is not enough place left for either the indices or vertices of the current element..., - if (spriteElementInfo.IndexCount > remainingIndexSpace || spriteElementInfo.VertexCount > remainingVertexSpace) + if (ResourceContext.IsIndexBufferDynamic) { - // if we haven't started the current batch yet, we restart at the beginning of the buffers. - if (batchSize == 0) - { - ResourceContext.VertexBufferPosition = 0; - ResourceContext.IndexBufferPosition = 0; - continue; - } - - // else we perform the draw call and batch remaining elements in next draw call. - break; + ResourceContext.IndexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.IndexCount * indexStructSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic)); + GraphicsContext.CommandList.SetIndexBuffer(ResourceContext.IndexBuffer, 0, indexStructSize == sizeof(int)); + lastFrameBuffers.Add(ResourceContext.IndexBuffer); } - - ++batchSize; + } + + int indexCount = 0, vertexCount = 0; + int batchStart = offset; + for (int vertexLeft = ResourceContext.VertexCount - ResourceContext.VertexBufferPosition, indexLeft = ResourceContext.IndexCount - ResourceContext.IndexBufferPosition; + offset < end && vertexCount < vertexLeft && indexCount < indexLeft; + offset++) + { + ref var spriteElementInfo = ref sprites[offset]; + vertexCount += spriteElementInfo.VertexCount; indexCount += spriteElementInfo.IndexCount; } - // Sets the data directly to the buffer in memory var offsetVertexInBytes = ResourceContext.VertexBufferPosition * vertexStructSize; var offsetIndexInBytes = ResourceContext.IndexBufferPosition * indexStructSize; - if (ResourceContext.VertexBufferPosition == 0) - { - if (ResourceContext.VertexBuffer != null) - GraphicsContext.Allocator.ReleaseReference(ResourceContext.VertexBuffer); - ResourceContext.VertexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.VertexCount * vertexStructSize, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic)); - GraphicsContext.CommandList.SetVertexBuffer(0, ResourceContext.VertexBuffer, 0, vertexStructSize); - } + var mappedIndices = new MappedResource(); + var mappedVertices = GraphicsContext.CommandList.MapSubresource(ResourceContext.VertexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetVertexInBytes, vertexCount * vertexStructSize); + if (ResourceContext.IsIndexBufferDynamic) + mappedIndices = GraphicsContext.CommandList.MapSubresource(ResourceContext.IndexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetIndexInBytes, indexCount * indexStructSize); - if (ResourceContext.IsIndexBufferDynamic && ResourceContext.IndexBufferPosition == 0) - { - if (ResourceContext.IndexBuffer != null) - GraphicsContext.Allocator.ReleaseReference(ResourceContext.IndexBuffer); - ResourceContext.IndexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.IndexCount * indexStructSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic)); - GraphicsContext.CommandList.SetIndexBuffer(ResourceContext.IndexBuffer, 0, indexStructSize == sizeof(int)); - } + var vertexPointer = mappedVertices.DataBox.DataPointer; + var indexPointer = mappedIndices.DataBox.DataPointer; - // ------------------------------------------------------------------------------------------------------------ - // CAUTION: Performance problem under x64 resolved by this special codepath: - // For some unknown reasons, It seems that writing directly to the pointer returned by the MapSubresource is - // extremely inefficient using x64 but using a temporary buffer and performing a mempcy to the locked region - // seems to be running at the same speed than x86 - // ------------------------------------------------------------------------------------------------------------ - // TODO Check again why we need this code - //if (IntPtr.Size == 8) - //{ - // if (x64TempBuffer == null) - // { - // x64TempBuffer = ToDispose(new DataBuffer(Utilities.SizeOf() * MaxBatchSize * VerticesPerSprite)); - // } - - // // Perform the update of all vertices on a temporary buffer - // var texturePtr = (VertexPositionColorTexture*)x64TempBuffer.DataPointer; - // for (int i = 0; i < batchSize; i++) - // { - // UpdateBufferValuesFromElementInfo(ref sprites[offset + i], ref texturePtr, deltaX, deltaY); - // } - - // // Then copy this buffer in one shot - // resourceContext.VertexBuffer.SetData(GraphicsDevice, new DataPointer(x64TempBuffer.DataPointer, batchSize * VerticesPerSprite * Utilities.SizeOf()), offsetInBytes, noOverwrite); - //} - //else + for (var i = batchStart; i < offset; i++) { - var mappedIndices = new MappedResource(); - var mappedVertices = GraphicsContext.CommandList.MapSubresource(ResourceContext.VertexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetVertexInBytes, vertexCount * vertexStructSize); - if (ResourceContext.IsIndexBufferDynamic) - mappedIndices = GraphicsContext.CommandList.MapSubresource(ResourceContext.IndexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetIndexInBytes, indexCount * indexStructSize); - - var vertexPointer = mappedVertices.DataBox.DataPointer; - var indexPointer = mappedIndices.DataBox.DataPointer; + ref var spriteElementInfo = ref sprites[i]; - for (var i = 0; i < batchSize; i++) - { - var spriteIndex = offset + i; - ref var spriteElementInfo = ref sprites[spriteIndex]; + UpdateBufferValuesFromElementInfo(ref spriteElementInfo, vertexPointer, indexPointer, ResourceContext.VertexBufferPosition); - UpdateBufferValuesFromElementInfo(ref spriteElementInfo, vertexPointer, indexPointer, ResourceContext.VertexBufferPosition); - - ResourceContext.VertexBufferPosition += spriteElementInfo.VertexCount; - vertexPointer += vertexStructSize * spriteElementInfo.VertexCount; - indexPointer += indexStructSize * spriteElementInfo.IndexCount; - } - - GraphicsContext.CommandList.UnmapSubresource(mappedVertices); - if (ResourceContext.IsIndexBufferDynamic) - GraphicsContext.CommandList.UnmapSubresource(mappedIndices); + ResourceContext.VertexBufferPosition += spriteElementInfo.VertexCount; + vertexPointer += vertexStructSize * spriteElementInfo.VertexCount; + indexPointer += indexStructSize * spriteElementInfo.IndexCount; } + GraphicsContext.CommandList.UnmapSubresource(mappedVertices); + if (ResourceContext.IsIndexBufferDynamic) + GraphicsContext.CommandList.UnmapSubresource(mappedIndices); + // Draw from the specified index GraphicsContext.CommandList.DrawIndexed(indexCount, ResourceContext.IndexBufferPosition); // Update position, offset and remaining count ResourceContext.IndexBufferPosition += indexCount; - offset += batchSize; - count -= batchSize; } }