From acea7b2e728787a0d83bbf83c8c1f042d2c32e7e Mon Sep 17 00:00:00 2001
From: chai <215380520@qq.com>
Date: Mon, 3 Jun 2024 10:15:45 +0800
Subject: + plugins project
---
.../source/MonoGame.Extended.Graphics/Batcher.cs | 387 +++++++++++++++++++++
1 file changed, 387 insertions(+)
create mode 100644 Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs
(limited to 'Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs')
diff --git a/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs
new file mode 100644
index 0000000..3cb5704
--- /dev/null
+++ b/Plugins/MonoGame.Extended/source/MonoGame.Extended.Graphics/Batcher.cs
@@ -0,0 +1,387 @@
+using System;
+using System.Runtime.CompilerServices;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace MonoGame.Extended.Graphics
+{
+ ///
+ /// Minimizes draw calls to a by sorting them and attempting to merge them together
+ /// before submitting them.
+ ///
+ /// The type of the information for a draw call.
+ ///
+ public abstract class Batcher : IDisposable
+ where TDrawCallInfo : struct, IBatchDrawCallInfo, IComparable
+ {
+ internal const int DefaultBatchMaximumDrawCallsCount = 2048;
+ private BlendState _blendState;
+ private SamplerState _samplerState;
+ private DepthStencilState _depthStencilState;
+ private RasterizerState _rasterizerState;
+ private readonly Effect _defaultEffect;
+ private Effect _currentEffect;
+ private Matrix? _viewMatrix;
+ private Matrix? _projectionMatrix;
+
+ ///
+ /// The array of structs currently enqueued.
+ ///
+ protected TDrawCallInfo[] DrawCalls;
+
+ ///
+ /// The number of structs currently enqueued.
+ ///
+ protected int EnqueuedDrawCallCount;
+
+ ///
+ /// Gets the associated with this .
+ ///
+ ///
+ /// The associated with this .
+ ///
+ public GraphicsDevice GraphicsDevice { get; }
+
+ ///
+ /// Gets a value indicating whether batching is currently in progress by being within a and
+ /// pair block of code.
+ ///
+ ///
+ /// true if batching has begun; otherwise, false.
+ ///
+ public bool HasBegun { get; internal set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The graphics device.
+ /// The default effect.
+ ///
+ /// The maximum number of structs that can be enqueued before a
+ ///
+ /// is required. The default value is 2048
.
+ ///
+ ///
+ /// is
+ /// null.
+ ///
+ ///
+ /// is less than or equal
+ /// 0
.
+ ///
+ protected Batcher(GraphicsDevice graphicsDevice, Effect defaultEffect,
+ int maximumDrawCallsCount = DefaultBatchMaximumDrawCallsCount)
+ {
+ if (graphicsDevice == null)
+ throw new ArgumentNullException(nameof(graphicsDevice));
+
+ if (maximumDrawCallsCount <= 0)
+ throw new ArgumentOutOfRangeException(nameof(maximumDrawCallsCount));
+
+ GraphicsDevice = graphicsDevice;
+ _defaultEffect = defaultEffect;
+ DrawCalls = new TDrawCallInfo[maximumDrawCallsCount];
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Releases unmanaged and - optionally - managed resources.
+ ///
+ ///
+ /// true to release both managed and unmanaged resources; false to release only
+ /// unmanaged resources.
+ ///
+ protected virtual void Dispose(bool diposing)
+ {
+ if (!diposing)
+ return;
+ _defaultEffect.Dispose();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected void EnsureHasBegun([CallerMemberName] string callerMemberName = null)
+ {
+ if (!HasBegun)
+ throw new InvalidOperationException(
+ $"The {nameof(Begin)} method must be called before the {callerMemberName} method can be called.");
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected void EnsureHasNotBegun([CallerMemberName] string callerMemberName = null)
+ {
+ if (HasBegun)
+ throw new InvalidOperationException(
+ $"The {nameof(End)} method must be called before the {callerMemberName} method can be called.");
+ }
+
+ ///
+ /// Begins the batch operation using an optional , ,
+ /// , , , world-to-view
+ /// , or view-to-projection .
+ ///
+ ///
+ ///
+ /// The default objects for , ,
+ /// , and are
+ /// , ,
+ /// and respectively.
+ /// Passing
+ /// null
for any of the previously mentioned parameters result in using their default object.
+ ///
+ ///
+ /// The to use for the , pair.
+ ///
+ /// The texture to use for the and
+ /// pair.
+ ///
+ ///
+ /// The to use for the and
+ /// pair.
+ ///
+ ///
+ /// The to use for the and
+ /// pair.
+ ///
+ /// The to use for the and pair.
+ ///
+ /// The world-to-view transformation matrix to use for the and
+ /// pair.
+ ///
+ ///
+ /// The view-to-projection transformation matrix to use for the and
+ /// pair.
+ ///
+ ///
+ /// cannot be invoked again until has been invoked.
+ ///
+ ///
+ ///
+ /// This method must be called before any enqueuing of draw calls. When all the geometry have been enqueued for
+ /// drawing, call .
+ ///
+ ///
+ public virtual void Begin(Matrix? viewMatrix = null, Matrix? projectionMatrix = null, BlendState blendState = null, SamplerState samplerState = null,
+ DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, Effect effect = null)
+ {
+ var viewMatrix1 = viewMatrix ?? Matrix.Identity;
+ var projectionMatrix1 = projectionMatrix ?? Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 0, -1);
+ Begin(ref viewMatrix1, ref projectionMatrix1, blendState, samplerState, depthStencilState, rasterizerState, effect);
+ }
+
+ ///
+ /// Begins the batch operation using an optional , ,
+ /// , , , world-to-view
+ /// , or view-to-projection .
+ ///
+ ///
+ ///
+ /// The default objects for , ,
+ /// , and are
+ /// , ,
+ /// and respectively.
+ /// Passing
+ /// null
for any of the previously mentioned parameters result in using their default object.
+ ///
+ ///
+ /// The to use for the , pair.
+ ///
+ /// The texture to use for the and
+ /// pair.
+ ///
+ ///
+ /// The to use for the and
+ /// pair.
+ ///
+ ///
+ /// The to use for the and
+ /// pair.
+ ///
+ /// The to use for the and pair.
+ ///
+ /// The world-to-view transformation matrix to use for the and
+ /// pair.
+ ///
+ ///
+ /// The view-to-projection transformation matrix to use for the and
+ /// pair.
+ ///
+ ///
+ /// cannot be invoked again until has been invoked.
+ ///
+ ///
+ ///
+ /// This method must be called before any enqueuing of draw calls. When all the geometry have been enqueued for
+ /// drawing, call .
+ ///
+ ///
+ public virtual void Begin(ref Matrix viewMatrix, ref Matrix projectionMatrix, BlendState blendState = null, SamplerState samplerState = null,
+ DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, Effect effect = null)
+ {
+ EnsureHasNotBegun();
+ HasBegun = true;
+
+ // Store the states to be applied on End()
+ // This ensures that two or more batchers will not affect each other
+ _blendState = blendState ?? BlendState.AlphaBlend;
+ _samplerState = samplerState ?? SamplerState.PointClamp;
+ _depthStencilState = depthStencilState ?? DepthStencilState.None;
+ _rasterizerState = rasterizerState ?? RasterizerState.CullCounterClockwise;
+ _currentEffect = effect ?? _defaultEffect;
+ _projectionMatrix = projectionMatrix;
+ _viewMatrix = viewMatrix;
+ }
+
+ ///
+ /// Flushes the batched geometry to the and restores it's state to how it was before
+ /// was called.
+ ///
+ ///
+ /// cannot be invoked until has been invoked.
+ ///
+ ///
+ ///
+ /// This method must be called after all enqueuing of draw calls.
+ ///
+ ///
+ public void End()
+ {
+ EnsureHasBegun();
+ Flush();
+ HasBegun = false;
+ }
+
+ ///
+ /// Sorts then submits the (sorted) enqueued draw calls to the for
+ /// rendering without ending the and pair.
+ ///
+ protected void Flush()
+ {
+ if (EnqueuedDrawCallCount == 0)
+ return;
+ SortDrawCallsAndBindBuffers();
+ ApplyStates();
+ SubmitDrawCalls();
+ RestoreStates();
+ }
+
+ ///
+ /// Sorts the enqueued draw calls and binds any used or to the .
+ ///
+ protected abstract void SortDrawCallsAndBindBuffers();
+
+ private void ApplyStates()
+ {
+ var oldBlendState = GraphicsDevice.BlendState;
+ var oldSamplerState = GraphicsDevice.SamplerStates[0];
+ var oldDepthStencilState = GraphicsDevice.DepthStencilState;
+ var oldRasterizerState = GraphicsDevice.RasterizerState;
+
+ GraphicsDevice.BlendState = _blendState;
+
+ GraphicsDevice.SamplerStates[0] = _samplerState;
+ GraphicsDevice.DepthStencilState = _depthStencilState;
+ GraphicsDevice.RasterizerState = _rasterizerState;
+
+ _blendState = oldBlendState;
+ _samplerState = oldSamplerState;
+ _depthStencilState = oldDepthStencilState;
+ _rasterizerState = oldRasterizerState;
+
+ var viewMatrix = _viewMatrix ?? Matrix.Identity;
+ var projectionMatrix = _projectionMatrix ??
+ Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width,
+ GraphicsDevice.Viewport.Height, 0, 0f, -1f);
+
+ var matrixChainEffect = _currentEffect as IMatrixChainEffect;
+ if (matrixChainEffect != null)
+ {
+ matrixChainEffect.World = Matrix.Identity;
+ matrixChainEffect.SetView(ref viewMatrix);
+ matrixChainEffect.SetProjection(ref projectionMatrix);
+ }
+ else
+ {
+ var effectMatrices = _currentEffect as IEffectMatrices;
+ if (effectMatrices == null)
+ return;
+ effectMatrices.World = Matrix.Identity;
+ effectMatrices.View = viewMatrix;
+ effectMatrices.Projection = projectionMatrix;
+ }
+ }
+
+ private void RestoreStates()
+ {
+ GraphicsDevice.BlendState = _blendState;
+ GraphicsDevice.SamplerStates[0] = _samplerState;
+ GraphicsDevice.DepthStencilState = _depthStencilState;
+ GraphicsDevice.RasterizerState = _rasterizerState;
+ }
+
+ ///
+ /// Enqueues draw call information.
+ ///
+ /// The draw call information.
+ ///
+ ///
+ /// If possible, the is merged with the last enqueued draw call information instead of
+ /// being
+ /// enqueued.
+ ///
+ ///
+ /// If the enqueue buffer is full, a is invoked and then afterwards
+ /// is enqueued.
+ ///
+ ///
+ protected void Enqueue(ref TDrawCallInfo drawCall)
+ {
+ if (EnqueuedDrawCallCount > 0 && drawCall.TryMerge(ref DrawCalls[EnqueuedDrawCallCount - 1]))
+ return;
+ if (EnqueuedDrawCallCount >= DrawCalls.Length)
+ Flush();
+ DrawCalls[EnqueuedDrawCallCount++] = drawCall;
+ }
+
+ /* It might be better to have derived classes just implement the for loop instead of having this virtual method call...
+ * However, if the derived class is only going to override this method once and the code is short, which should both be
+ * true, then maybe we can get away with this virtual method call by having it inlined. So tell the JIT or AOT compiler
+ * we would like it be so. This does NOT guarantee the compiler will respect our wishes.
+ */
+
+ ///
+ /// Submits a draw operation to the using the specified .
+ ///
+ /// The draw call information.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ protected abstract void InvokeDrawCall(ref TDrawCallInfo drawCall);
+
+ private void SubmitDrawCalls()
+ {
+ if (EnqueuedDrawCallCount == 0)
+ return;
+
+ for (var i = 0; i < EnqueuedDrawCallCount; i++)
+ {
+ DrawCalls[i].SetState(_currentEffect);
+
+ foreach (var pass in _currentEffect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+ InvokeDrawCall(ref DrawCalls[i]);
+ }
+ }
+
+ Array.Clear(DrawCalls, 0, EnqueuedDrawCallCount);
+ EnqueuedDrawCallCount = 0;
+ }
+ }
+}
+
\ No newline at end of file
--
cgit v1.1-26-g67d0