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; } } }