Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 46 additions & 89 deletions sources/engine/Stride.Graphics/BatchBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Buffer> lastFrameBuffers;

private readonly int vertexStructSize;
private readonly int indexStructSize;
Expand Down Expand Up @@ -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<DeviceResourceContext>(() => new DeviceResourceContext(graphicsDevice, vertexDeclaration, resourceBufferInfo), true));

lastFrameBuffers = new();
}

protected override void Destroy()
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<VertexPositionColorTexture>() * 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<VertexPositionColorTexture>()), 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;
}
}

Expand Down
Loading