mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-26 00:45:20 +00:00
Create Graphics library. (#316)
* Fix BoundingRect unit tests. * Create `graphics` library.
This commit is contained in:
committed by
Dylan Wilson
parent
d1ffba93f2
commit
d0c621d0dc
@@ -0,0 +1,160 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user