Skip to content

Commit b3531fe

Browse files
authored
Merge pull request #2887 from Eideren/ui_batch
perf: Improve UI batch performance
2 parents b94a072 + b9528b8 commit b3531fe

1 file changed

Lines changed: 46 additions & 89 deletions

File tree

sources/engine/Stride.Graphics/BatchBase.cs

Lines changed: 46 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public ElementInfo(int vertexCount, int indexCount, in TDrawInfo drawInfo, float
9191
private ElementInfo[] drawsQueue;
9292
private int drawsQueueCount;
9393
private Texture[] drawTextures;
94+
private List<Buffer> lastFrameBuffers;
9495

9596
private readonly int vertexStructSize;
9697
private readonly int indexStructSize;
@@ -141,6 +142,8 @@ protected BatchBase(GraphicsDevice device, EffectBytecode defaultEffectByteCode,
141142
// Creates the vertex buffer (shared by within a device context).
142143
// TODO: find a better way to do that, and check resource disposal
143144
ResourceContextPool = graphicsDevice.GetOrCreateSharedData(resourceBufferInfo.ResourceKey, d => new ThreadLocal<DeviceResourceContext>(() => new DeviceResourceContext(graphicsDevice, vertexDeclaration, resourceBufferInfo), true));
145+
146+
lastFrameBuffers = new();
144147
}
145148

146149
protected override void Destroy()
@@ -172,6 +175,10 @@ protected void Begin(GraphicsContext graphicsContext, EffectInstance effect, Spr
172175
{
173176
CheckEndHasBeenCalled("begin");
174177

178+
foreach (var buffer in lastFrameBuffers)
179+
GraphicsContext.Allocator.ReleaseReference(buffer);
180+
lastFrameBuffers.Clear();
181+
175182
ResourceContext = ResourceContextPool.Value;
176183

177184
GraphicsContext = graphicsContext;
@@ -418,120 +425,70 @@ private void DrawBatchPerTexture(Texture texture, ElementInfo[] sprites, int off
418425

419426
private void DrawBatchPerTextureAndPass(ElementInfo[] sprites, int offset, int count)
420427
{
421-
while (count > 0)
428+
ResourceContext.VertexBufferPosition = ResourceContext.VertexCount;
429+
ResourceContext.IndexBufferPosition = ResourceContext.IndexCount;
430+
for (int end = offset + count; offset < end;)
422431
{
423-
// How many index/vertex do we want to draw?
424-
var indexCount = 0;
425-
var vertexCount = 0;
426-
var batchSize = 0;
427-
428-
while (batchSize < count)
432+
if (ResourceContext.VertexBufferPosition == ResourceContext.VertexCount || ResourceContext.IndexBufferPosition == ResourceContext.IndexCount) // Buffer is full, fetch the next ones
429433
{
430-
var spriteIndex = offset + batchSize;
431-
ref var spriteElementInfo = ref sprites[spriteIndex];
434+
ResourceContext.VertexBufferPosition = 0;
435+
ResourceContext.IndexBufferPosition = 0;
432436

433-
// How many sprites does the D3D vertex buffer have room for?
434-
var remainingVertexSpace = ResourceContext.VertexCount - ResourceContext.VertexBufferPosition - vertexCount;
435-
var remainingIndexSpace = ResourceContext.IndexCount - ResourceContext.IndexBufferPosition - indexCount;
437+
ResourceContext.VertexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.VertexCount * vertexStructSize, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic));
438+
GraphicsContext.CommandList.SetVertexBuffer(0, ResourceContext.VertexBuffer, 0, vertexStructSize);
439+
lastFrameBuffers.Add(ResourceContext.VertexBuffer);
436440

437-
// if there is not enough place left for either the indices or vertices of the current element...,
438-
if (spriteElementInfo.IndexCount > remainingIndexSpace || spriteElementInfo.VertexCount > remainingVertexSpace)
441+
if (ResourceContext.IsIndexBufferDynamic)
439442
{
440-
// if we haven't started the current batch yet, we restart at the beginning of the buffers.
441-
if (batchSize == 0)
442-
{
443-
ResourceContext.VertexBufferPosition = 0;
444-
ResourceContext.IndexBufferPosition = 0;
445-
continue;
446-
}
447-
448-
// else we perform the draw call and batch remaining elements in next draw call.
449-
break;
443+
ResourceContext.IndexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.IndexCount * indexStructSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic));
444+
GraphicsContext.CommandList.SetIndexBuffer(ResourceContext.IndexBuffer, 0, indexStructSize == sizeof(int));
445+
lastFrameBuffers.Add(ResourceContext.IndexBuffer);
450446
}
451-
452-
++batchSize;
447+
}
448+
449+
int indexCount = 0, vertexCount = 0;
450+
int batchStart = offset;
451+
for (int vertexLeft = ResourceContext.VertexCount - ResourceContext.VertexBufferPosition, indexLeft = ResourceContext.IndexCount - ResourceContext.IndexBufferPosition;
452+
offset < end && vertexCount < vertexLeft && indexCount < indexLeft;
453+
offset++)
454+
{
455+
ref var spriteElementInfo = ref sprites[offset];
456+
453457
vertexCount += spriteElementInfo.VertexCount;
454458
indexCount += spriteElementInfo.IndexCount;
455459
}
456460

457-
// Sets the data directly to the buffer in memory
458461
var offsetVertexInBytes = ResourceContext.VertexBufferPosition * vertexStructSize;
459462
var offsetIndexInBytes = ResourceContext.IndexBufferPosition * indexStructSize;
460463

461-
if (ResourceContext.VertexBufferPosition == 0)
462-
{
463-
if (ResourceContext.VertexBuffer != null)
464-
GraphicsContext.Allocator.ReleaseReference(ResourceContext.VertexBuffer);
465-
ResourceContext.VertexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.VertexCount * vertexStructSize, BufferFlags.VertexBuffer, GraphicsResourceUsage.Dynamic));
466-
GraphicsContext.CommandList.SetVertexBuffer(0, ResourceContext.VertexBuffer, 0, vertexStructSize);
467-
}
464+
var mappedIndices = new MappedResource();
465+
var mappedVertices = GraphicsContext.CommandList.MapSubresource(ResourceContext.VertexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetVertexInBytes, vertexCount * vertexStructSize);
466+
if (ResourceContext.IsIndexBufferDynamic)
467+
mappedIndices = GraphicsContext.CommandList.MapSubresource(ResourceContext.IndexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetIndexInBytes, indexCount * indexStructSize);
468468

469-
if (ResourceContext.IsIndexBufferDynamic && ResourceContext.IndexBufferPosition == 0)
470-
{
471-
if (ResourceContext.IndexBuffer != null)
472-
GraphicsContext.Allocator.ReleaseReference(ResourceContext.IndexBuffer);
473-
ResourceContext.IndexBuffer = GraphicsContext.Allocator.GetTemporaryBuffer(new BufferDescription(ResourceContext.IndexCount * indexStructSize, BufferFlags.IndexBuffer, GraphicsResourceUsage.Dynamic));
474-
GraphicsContext.CommandList.SetIndexBuffer(ResourceContext.IndexBuffer, 0, indexStructSize == sizeof(int));
475-
}
469+
var vertexPointer = mappedVertices.DataBox.DataPointer;
470+
var indexPointer = mappedIndices.DataBox.DataPointer;
476471

477-
// ------------------------------------------------------------------------------------------------------------
478-
// CAUTION: Performance problem under x64 resolved by this special codepath:
479-
// For some unknown reasons, It seems that writing directly to the pointer returned by the MapSubresource is
480-
// extremely inefficient using x64 but using a temporary buffer and performing a mempcy to the locked region
481-
// seems to be running at the same speed than x86
482-
// ------------------------------------------------------------------------------------------------------------
483-
// TODO Check again why we need this code
484-
//if (IntPtr.Size == 8)
485-
//{
486-
// if (x64TempBuffer == null)
487-
// {
488-
// x64TempBuffer = ToDispose(new DataBuffer(Utilities.SizeOf<VertexPositionColorTexture>() * MaxBatchSize * VerticesPerSprite));
489-
// }
490-
491-
// // Perform the update of all vertices on a temporary buffer
492-
// var texturePtr = (VertexPositionColorTexture*)x64TempBuffer.DataPointer;
493-
// for (int i = 0; i < batchSize; i++)
494-
// {
495-
// UpdateBufferValuesFromElementInfo(ref sprites[offset + i], ref texturePtr, deltaX, deltaY);
496-
// }
497-
498-
// // Then copy this buffer in one shot
499-
// resourceContext.VertexBuffer.SetData(GraphicsDevice, new DataPointer(x64TempBuffer.DataPointer, batchSize * VerticesPerSprite * Utilities.SizeOf<VertexPositionColorTexture>()), offsetInBytes, noOverwrite);
500-
//}
501-
//else
472+
for (var i = batchStart; i < offset; i++)
502473
{
503-
var mappedIndices = new MappedResource();
504-
var mappedVertices = GraphicsContext.CommandList.MapSubresource(ResourceContext.VertexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetVertexInBytes, vertexCount * vertexStructSize);
505-
if (ResourceContext.IsIndexBufferDynamic)
506-
mappedIndices = GraphicsContext.CommandList.MapSubresource(ResourceContext.IndexBuffer, 0, MapMode.WriteNoOverwrite, false, offsetIndexInBytes, indexCount * indexStructSize);
507-
508-
var vertexPointer = mappedVertices.DataBox.DataPointer;
509-
var indexPointer = mappedIndices.DataBox.DataPointer;
474+
ref var spriteElementInfo = ref sprites[i];
510475

511-
for (var i = 0; i < batchSize; i++)
512-
{
513-
var spriteIndex = offset + i;
514-
ref var spriteElementInfo = ref sprites[spriteIndex];
476+
UpdateBufferValuesFromElementInfo(ref spriteElementInfo, vertexPointer, indexPointer, ResourceContext.VertexBufferPosition);
515477

516-
UpdateBufferValuesFromElementInfo(ref spriteElementInfo, vertexPointer, indexPointer, ResourceContext.VertexBufferPosition);
517-
518-
ResourceContext.VertexBufferPosition += spriteElementInfo.VertexCount;
519-
vertexPointer += vertexStructSize * spriteElementInfo.VertexCount;
520-
indexPointer += indexStructSize * spriteElementInfo.IndexCount;
521-
}
522-
523-
GraphicsContext.CommandList.UnmapSubresource(mappedVertices);
524-
if (ResourceContext.IsIndexBufferDynamic)
525-
GraphicsContext.CommandList.UnmapSubresource(mappedIndices);
478+
ResourceContext.VertexBufferPosition += spriteElementInfo.VertexCount;
479+
vertexPointer += vertexStructSize * spriteElementInfo.VertexCount;
480+
indexPointer += indexStructSize * spriteElementInfo.IndexCount;
526481
}
527482

483+
GraphicsContext.CommandList.UnmapSubresource(mappedVertices);
484+
if (ResourceContext.IsIndexBufferDynamic)
485+
GraphicsContext.CommandList.UnmapSubresource(mappedIndices);
486+
528487
// Draw from the specified index
529488
GraphicsContext.CommandList.DrawIndexed(indexCount, ResourceContext.IndexBufferPosition);
530489

531490
// Update position, offset and remaining count
532491
ResourceContext.IndexBufferPosition += indexCount;
533-
offset += batchSize;
534-
count -= batchSize;
535492
}
536493
}
537494

0 commit comments

Comments
 (0)