mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-24 20:12:23 +00:00
* Fix BoundingRect unit tests. * Create `graphics` library.
160 lines
7.9 KiB
C#
160 lines
7.9 KiB
C#
using System;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
namespace MonoGame.Extended.Graphics.Batching
|
|
{
|
|
internal sealed class DeferredBatchCommandQueue<TVertexType, TCommandData> :
|
|
BatchCommandQueue<TVertexType, TCommandData>
|
|
where TVertexType : struct, IVertexType where TCommandData : struct, IBatchDrawCommandData<TCommandData>
|
|
{
|
|
private readonly BatchDrawCommand<TCommandData>[] _commands;
|
|
private readonly GeometryBuffer<TVertexType> _geometryBuffer;
|
|
private readonly int _maximumCommandsCount;
|
|
private readonly ushort[] _sortedIndices;
|
|
private int _commandsCount;
|
|
private BatchDrawCommand<TCommandData> _currentCommand;
|
|
internal BatchSortMode SortMode;
|
|
|
|
internal DeferredBatchCommandQueue(GraphicsDevice graphicsDevice,
|
|
BatchCommandDrawer<TVertexType, TCommandData> commandDrawer, int maximumCommandsCount)
|
|
: base(graphicsDevice, commandDrawer)
|
|
{
|
|
_commands = new BatchDrawCommand<TCommandData>[maximumCommandsCount];
|
|
_geometryBuffer = commandDrawer.GeometryBuffer;
|
|
_sortedIndices = new ushort[_geometryBuffer._indices.Length];
|
|
_maximumCommandsCount = maximumCommandsCount;
|
|
}
|
|
|
|
protected internal override void Flush()
|
|
{
|
|
// quit early if there are no deferred commands to process
|
|
if (_commandsCount < 0)
|
|
return;
|
|
|
|
var vertexBuffer = _geometryBuffer.VertexBuffer;
|
|
// upload the vertices to the GPU's memory from CPU's memory
|
|
vertexBuffer.SetData(_geometryBuffer._vertices, 0, _geometryBuffer._vertexCount);
|
|
// tell the graphics API we want to use that buffer of vertices for rendering
|
|
GraphicsDevice.SetVertexBuffer(vertexBuffer);
|
|
|
|
// conditional code depending on deferred mode
|
|
// ReSharper disable once SwitchStatementMissingSomeCases
|
|
switch (SortMode)
|
|
{
|
|
case BatchSortMode.Deferred:
|
|
FlushDeferred();
|
|
break;
|
|
case BatchSortMode.DeferredSorted:
|
|
FlushDeferredSorted();
|
|
break;
|
|
}
|
|
|
|
// prevent a highly unlikely, but possible, memory leak where the current command would be holding onto some reference(s)
|
|
_currentCommand.Data.SetReferencesToNull();
|
|
|
|
// clear the geometry buffer if it is dynamic
|
|
if (_geometryBuffer.BufferType == GeometryBufferType.Dynamic)
|
|
_geometryBuffer.Clear();
|
|
|
|
// reset the commands count
|
|
// we don't clear the array of commands because we will just overwrite the values anyways
|
|
_commandsCount = 0;
|
|
}
|
|
|
|
private void FlushDeferred()
|
|
{
|
|
var indexBuffer = _geometryBuffer.IndexBuffer;
|
|
// upload the indices to the GPU's memory from CPU's memory
|
|
indexBuffer.SetData(_geometryBuffer._indices, 0, _geometryBuffer._indexCount);
|
|
// tell the graphics API we want to use that buffer of indices for rendering
|
|
GraphicsDevice.Indices = indexBuffer;
|
|
|
|
// ReSharper disable once ForCanBeConvertedToForeach
|
|
for (var index = 0; index < _commandsCount; index++)
|
|
CommandDrawer.Draw(ref _commands[index]);
|
|
}
|
|
|
|
private void FlushDeferredSorted()
|
|
{
|
|
// sort the commands
|
|
Array.Sort(_commands, 0, _commandsCount);
|
|
|
|
_currentCommand = _commands[0];
|
|
ushort sortedIndiesCount = 0;
|
|
var newCommandsCount = 1;
|
|
|
|
// change the indices used by the command to use the sorted indices array so the indices are sequential for the graphics API
|
|
_commands[0].StartIndex = 0;
|
|
// get the number of vertices used for the current command, each index represents a vertex
|
|
var commandIndicesCount = PrimitiveType.GetVerticesCount(_currentCommand.PrimitiveCount);
|
|
// copy the indices of the current command into our sorted indices array so the indices are sequential for the graphics API
|
|
Array.Copy(_geometryBuffer._indices, _currentCommand.StartIndex, _sortedIndices, sortedIndiesCount,
|
|
commandIndicesCount);
|
|
sortedIndiesCount += (ushort) commandIndicesCount;
|
|
|
|
// iterate through sorted commands checking if any can now be merged to reduce expensive draw calls to the graphics API
|
|
// this might need to be changed for next-gen graphics API (Vulkan, Metal, DirectX 11) where the draw calls are not so expensive
|
|
for (var index = 1; index < _commandsCount; index++)
|
|
{
|
|
var command = _commands[index];
|
|
|
|
// get the number of vertices used for the command, each index represents a vertex
|
|
commandIndicesCount = PrimitiveType.GetVerticesCount(command.PrimitiveCount);
|
|
|
|
if (_currentCommand.CanMergeWith(command.SortKey, ref command.Data))
|
|
{
|
|
// increase the number of primitives for the current command by the amount of the merged command
|
|
_commands[newCommandsCount - 1].PrimitiveCount =
|
|
_currentCommand.PrimitiveCount += command.PrimitiveCount;
|
|
}
|
|
else
|
|
{
|
|
// could not merge command
|
|
newCommandsCount++;
|
|
// change the indices used by the command to use the sorted indices array so the indices are sequential for the graphics API
|
|
_commands[newCommandsCount - 1].StartIndex = sortedIndiesCount;
|
|
// set the current command to this command so we can check if the next command can be merged
|
|
_currentCommand = command;
|
|
}
|
|
|
|
// copy the indices of the command into our sorted indices array so the merged indices are sequential for the graphics API
|
|
Array.Copy(_geometryBuffer._indices, command.StartIndex, _sortedIndices, sortedIndiesCount,
|
|
commandIndicesCount);
|
|
sortedIndiesCount += (ushort) commandIndicesCount;
|
|
}
|
|
|
|
var indexBuffer = _geometryBuffer.IndexBuffer;
|
|
// upload the indices to the GPU's memory from CPU's memory
|
|
indexBuffer.SetData(_sortedIndices, 0, sortedIndiesCount);
|
|
// tell the graphics API we want to use that buffer of indices for rendering
|
|
GraphicsDevice.Indices = indexBuffer;
|
|
|
|
// ReSharper disable once ForCanBeConvertedToForeach
|
|
for (var index = 0; index < newCommandsCount; index++)
|
|
CommandDrawer.Draw(ref _commands[index]);
|
|
}
|
|
|
|
internal override void EnqueueDrawCommand(ushort startIndex, ushort primitiveCount, float sortKey,
|
|
ref TCommandData data)
|
|
{
|
|
// merge draw commands if possible to reduce expensive draw calls to the graphics API
|
|
// this might need to be changed for next-gen graphics API (Vulkan, Metal, DirectX 11) where the draw calls are not so expensive
|
|
if (_currentCommand.CanMergeWith(sortKey, ref data))
|
|
{
|
|
// since indices are added in sequential fasion we can just increase the primitive count of the current command
|
|
_commands[_commandsCount - 1].PrimitiveCount = _currentCommand.PrimitiveCount += primitiveCount;
|
|
return;
|
|
}
|
|
|
|
// overflow check
|
|
if (_commandsCount >= _maximumCommandsCount)
|
|
throw new BatchCommandQueueOverflowException(_maximumCommandsCount);
|
|
|
|
// could not merge draw command, initialize a new one
|
|
_currentCommand = new BatchDrawCommand<TCommandData>(startIndex, primitiveCount, sortKey, data);
|
|
|
|
// append the command to the array
|
|
_commands[_commandsCount++] = _currentCommand;
|
|
}
|
|
}
|
|
} |