using System;
using Microsoft.Xna.Framework.Graphics;
namespace MonoGame.Extended.Graphics
{
///
/// Enables method calls to be
/// deferred so they can be sorted to minimize state changes.
///
/// The type of the draw call data.
///
public abstract class QueueRenderer : Renderer where TDrawCallData : struct, IDrawCallData
{
internal const int DefaultMaximumDrawCallsCount = 2048;
///
/// Gets or sets the number of derferred draw calls currently enqueued.
///
///
/// The number of deferred draw calls currently enqueued.
///
protected int EnqueuedDrawCallsCount { get; set; }
///
/// Gets the deferred draw calls.
///
///
/// The draw deferred draw calls.
///
protected DrawCall[] DrawCalls { get; }
///
/// Initializes a new instance of the class.
///
/// The graphics device.
/// The default effect.
///
/// The maximum number of draw calls that can be deferred. The default value is 2024.
///
/// , or is null.
///
/// is less than or equal
/// 0.
///
protected QueueRenderer(GraphicsDevice graphicsDevice, Effect defaultEffect,
int maximumDrawCallsCount = DefaultMaximumDrawCallsCount)
: base(graphicsDevice, defaultEffect)
{
if (maximumDrawCallsCount <= 0)
throw new ArgumentOutOfRangeException(nameof(maximumDrawCallsCount));
DrawCalls = new DrawCall[maximumDrawCallsCount];
}
///
/// Attempts to enqueue a method call
/// to for sorting.
///
/// The draw call.
/// false if the enqueue buffer is full; otherwise, true.
protected bool TryEnqueueDrawCall(ref DrawCall drawCall)
{
if (EnqueuedDrawCallsCount >= DrawCalls.Length)
return false;
DrawCalls[EnqueuedDrawCallsCount++] = drawCall;
return true;
}
///
/// Sorts then submits the (sorted) enqueued to the for
/// rendering without ending the rendering pass.
///
public virtual void Flush()
{
ApplyStates();
Array.Sort(DrawCalls, 0, EnqueuedDrawCallsCount);
SubmitDrawCalls();
}
///
/// Ends the rendering pass.
///
///
/// cannot be invoked until has been invoked.
///
///
///
/// This method must be called after all enqueuing of draw calls.
///
///
public override void End()
{
EnsureHasBegun();
Flush();
HasBegun = false;
}
///
/// Submits the draw calls to the .
///
protected virtual void SubmitDrawCalls()
{
// do the actual rendering using the graphics API
for (var i = 0; i < EnqueuedDrawCallsCount; i++)
{
// change the rendering state if necessary
DrawCalls[i].Data.ApplyTo(Effect);
foreach (var pass in Effect.CurrentTechnique.Passes)
{
pass.Apply();
var primitiveType = DrawCalls[i].PrimitiveType;
var baseVertex = DrawCalls[i].BaseVertex;
var startIndex = DrawCalls[i].StartIndex;
var primitiveCount = DrawCalls[i].PrimitiveCount;
GraphicsDevice.DrawIndexedPrimitives(primitiveType, baseVertex, startIndex, primitiveCount);
}
// prevent a possible memory leak where the draw calls would be holding onto some reference(s)
DrawCalls[i].Data.SetReferencesToNull();
}
EnqueuedDrawCallsCount = 0;
}
}
}