25 using System.Collections.Generic;
27 using SiliconStudio.Core;
28 using SiliconStudio.Paradox.Effects.Modules;
29 using SiliconStudio.Paradox.Graphics.Internals;
31 namespace SiliconStudio.
Paradox.Graphics
37 public abstract class BatchBase<TDrawInfo> :
ComponentBase where TDrawInfo : struct
42 protected struct ElementInfo
64 public ElementInfo(
int vertexCount,
int indexCount, ref TDrawInfo drawInfo,
float depth = 0)
66 VertexCount = vertexCount;
67 IndexCount = indexCount;
83 private EffectParameterResourceBinding? texture0Updater;
84 private EffectParameterResourceBinding? texture1Updater;
86 private int[] sortIndices;
87 private ElementInfo[] sortedDraws;
88 private ElementInfo[] drawsQueue;
89 private int drawsQueueCount;
90 private DrawTextures[] drawTextures;
92 private readonly
int vertexStructSize;
93 private readonly
int indexStructSize;
99 private bool isBeginCalled;
107 protected TextureIdComparer TextureComparer {
get; set; }
108 protected QueueComparer<ElementInfo> BackToFrontComparer {
get; set; }
109 protected QueueComparer<ElementInfo> FrontToBackComparer {
get; set; }
111 internal const float DepthBiasShiftOneUnit = 0.0001f;
115 if (resourceBufferInfo == null)
throw new ArgumentNullException(
"resourceBufferInfo");
116 if (vertexDeclaration == null)
throw new ArgumentNullException(
"vertexDeclaration");
119 DefaultEffect =
new Effect(device, defaultEffectByteCode) { Name =
"BatchDefaultEffect"};
121 drawsQueue =
new ElementInfo[resourceBufferInfo.BatchCapacity];
122 drawTextures =
new DrawTextures[resourceBufferInfo.BatchCapacity];
124 TextureComparer =
new TextureIdComparer();
125 BackToFrontComparer =
new SpriteBackToFrontComparer();
126 FrontToBackComparer =
new SpriteFrontToBackComparer();
129 indexStructSize = indexSize;
130 this.vertexDeclaration = vertexDeclaration;
131 vertexStructSize = vertexDeclaration.CalculateSize();
134 ResourceContext = GraphicsDevice.GetOrCreateSharedData(GraphicsDeviceSharedDataType.PerContext, resourceBufferInfo.ResourceKey, () =>
new DeviceResourceContext(
GraphicsDevice, DefaultEffect, vertexDeclaration, resourceBufferInfo, indexStructSize));
151 CheckEndHasBeenCalled(
"begin");
153 SortMode = sessionSortMode;
158 StencilReferenceValue = stencilValue;
160 Effect = effect ?? DefaultEffect;
162 texture0Updater = null;
163 texture1Updater = null;
172 if (ResourceContext.IsInImmediateMode)
174 throw new InvalidOperationException(
"Only one SpriteBatch at a time can use SpriteSortMode.Immediate");
177 PrepareForRendering();
179 ResourceContext.IsInImmediateMode =
true;
183 isBeginCalled =
true;
189 var localSamplerState =
SamplerState ?? GraphicsDevice.SamplerStates.LinearClamp;
192 Effect.Parameters.Set(TexturingKeys.Sampler, localSamplerState);
196 GraphicsDevice.SetBlendState(
BlendState ?? GraphicsDevice.BlendStates.AlphaBlend);
197 GraphicsDevice.SetDepthStencilState(
DepthStencilState ?? GraphicsDevice.DepthStencilStates.Default, StencilReferenceValue);
198 GraphicsDevice.SetRasterizerState(
RasterizerState ?? GraphicsDevice.RasterizerStates.CullBack);
201 GraphicsDevice.SetVertexArrayObject(ResourceContext.VertexArrayObject);
206 ResourceContext.VertexBufferPosition = 0;
214 throw new InvalidOperationException(
"Begin must be called before " + functionName);
222 throw new InvalidOperationException(
"End must be called before " + functionName);
231 CheckBeginHasBeenCalled(
"End");
235 ResourceContext.IsInImmediateMode =
false;
237 else if (drawsQueueCount > 0)
240 if (ResourceContext.IsInImmediateMode)
242 throw new InvalidOperationException(
"Cannot end one SpriteBatch while another is using SpriteSortMode.Immediate");
246 PrepareForRendering();
251 isBeginCalled =
false;
254 private void SortSprites()
260 case SpriteSortMode.Texture:
261 TextureComparer.SpriteTextures = drawTextures;
262 comparer = TextureComparer;
265 case SpriteSortMode.BackToFront:
266 BackToFrontComparer.ImageInfos = drawsQueue;
267 comparer = BackToFrontComparer;
270 case SpriteSortMode.FrontToBack:
271 FrontToBackComparer.ImageInfos = drawsQueue;
272 comparer = FrontToBackComparer;
275 throw new NotSupportedException();
278 if ((sortIndices == null) || (sortIndices.Length < drawsQueueCount))
280 sortIndices =
new int[drawsQueueCount];
281 sortedDraws =
new ElementInfo[drawsQueueCount];
285 for (
int i = 0; i < drawsQueueCount; i++)
290 Array.Sort(sortIndices, 0, drawsQueueCount, comparer);
293 private void FlushBatch()
295 ElementInfo[] spriteQueueForBatch;
300 spriteQueueForBatch = drawsQueue;
306 spriteQueueForBatch = sortedDraws;
311 var previousTexture =
new DrawTextures();
312 for (
int i = 0; i < drawsQueueCount; i++)
314 DrawTextures texture;
318 texture = drawTextures[i];
323 int index = sortIndices[i];
324 spriteQueueForBatch[i] = drawsQueue[index];
327 texture = drawTextures[index];
330 if (DrawTextures.NotEqual(ref texture, ref previousTexture))
334 DrawBatchPerTexture(previousTexture.Texture0, previousTexture.Texture1, spriteQueueForBatch, offset, i - offset);
338 previousTexture = texture;
343 DrawBatchPerTexture(previousTexture.Texture0, previousTexture.Texture1, spriteQueueForBatch, offset, drawsQueueCount - offset);
346 Array.Clear(drawTextures, 0, drawsQueueCount);
354 Array.Clear(sortedDraws, 0, sortedDraws.Length);
358 private void DrawBatchPerTexture(Texture texture, Texture texture1, ElementInfo[] sprites,
int offset,
int count)
364 if (texture0Updater.HasValue)
365 texture0Updater.Value.ApplyParameter(GraphicsDevice, texture);
366 if (texture1Updater.HasValue)
367 texture1Updater.Value.ApplyParameter(GraphicsDevice, texture1);
370 DrawBatchPerTextureAndPass(sprites, offset, count);
373 private void DrawBatchPerTextureAndPass(ElementInfo[] sprites,
int offset,
int count)
382 while (batchSize < count)
384 var spriteIndex = offset + batchSize;
387 var remainingVertexSpace = ResourceContext.VertexCount - ResourceContext.VertexBufferPosition - vertexCount;
388 var remainingIndexSpace = ResourceContext.IndexCount - ResourceContext.IndexBufferPosition - indexCount;
391 if (sprites[spriteIndex].IndexCount > remainingIndexSpace || sprites[spriteIndex].VertexCount > remainingVertexSpace)
396 ResourceContext.VertexBufferPosition = 0;
397 ResourceContext.IndexBufferPosition = 0;
406 vertexCount += sprites[spriteIndex].VertexCount;
407 indexCount += sprites[spriteIndex].IndexCount;
411 var offsetVertexInBytes = ResourceContext.VertexBufferPosition * vertexStructSize;
412 var offsetIndexInBytes = ResourceContext.IndexBufferPosition * indexStructSize;
414 var noOverwriteVertex = ResourceContext.VertexBufferPosition == 0 ? MapMode.WriteDiscard : MapMode.WriteNoOverwrite;
415 var noOverwriteIndex = ResourceContext.IndexBufferPosition == 0 ? MapMode.WriteDiscard : MapMode.WriteNoOverwrite;
443 var mappedIndices =
new MappedResource();
444 var mappedVertices = GraphicsDevice.MapSubresource(ResourceContext.VertexBuffer, 0, noOverwriteVertex,
false, offsetVertexInBytes, vertexCount * vertexStructSize);
445 if (ResourceContext.IsIndexBufferDynamic)
446 mappedIndices = GraphicsDevice.MapSubresource(ResourceContext.IndexBuffer, 0, noOverwriteIndex,
false, offsetIndexInBytes, indexCount * indexStructSize);
448 var vertexPointer = mappedVertices.DataBox.DataPointer;
449 var indexPointer = mappedIndices.DataBox.DataPointer;
451 for (var i = 0; i < batchSize; i++)
453 var spriteIndex = offset + i;
455 UpdateBufferValuesFromElementInfo(ref sprites[spriteIndex], vertexPointer, indexPointer, ResourceContext.VertexBufferPosition);
457 ResourceContext.VertexBufferPosition += sprites[spriteIndex].VertexCount;
458 vertexPointer += vertexStructSize * sprites[spriteIndex].VertexCount;
459 indexPointer += indexStructSize * sprites[spriteIndex].IndexCount;
462 GraphicsDevice.UnmapSubresource(mappedVertices);
463 if (ResourceContext.IsIndexBufferDynamic)
464 GraphicsDevice.UnmapSubresource(mappedIndices);
468 GraphicsDevice.DrawIndexed(PrimitiveType.TriangleList, indexCount, ResourceContext.IndexBufferPosition);
471 ResourceContext.IndexBufferPosition += indexCount;
480 CheckBeginHasBeenCalled(
"draw");
483 if (drawsQueueCount >= drawsQueue.Length)
485 Array.Resize(ref drawsQueue, drawsQueue.Length * 2);
489 drawsQueue[drawsQueueCount] = elementInfo;
494 DrawBatchPerTexture(texture, texture1, drawsQueue, 0, 1);
498 if (drawTextures.Length < drawsQueue.Length)
500 Array.Resize(ref drawTextures, drawsQueue.Length);
502 drawTextures[drawsQueueCount].Texture0 = texture;
503 drawTextures[drawsQueueCount].Texture1 = texture1;
515 protected abstract void UpdateBufferValuesFromElementInfo(ref ElementInfo elementInfo, IntPtr vertexPointer, IntPtr indexPointer,
int vexterStartOffset);
519 protected struct DrawTextures
524 public static bool NotEqual(ref DrawTextures left, ref DrawTextures right)
526 return left.Texture0 != right.Texture0 || left.Texture1 != right.Texture1;
533 protected class ResourceBufferInfo
544 public int BatchCapacity {
get; set; }
549 public int VertexCount {
get;
protected set; }
554 public int IndexCount {
get;
private set; }
564 public bool IsIndexBufferDynamic {
get {
return StaticIndices == null; } }
574 return new ResourceBufferInfo(resourceKey, null, indexCount, vertexCount);
585 return new ResourceBufferInfo(resourceKey, staticIndices, 0, vertexCount);
588 protected ResourceBufferInfo(
string resourceKey,
short[] staticIndices,
int indexCount,
int vertexCount)
590 if (staticIndices != null)
591 indexCount = staticIndices.Length;
594 ResourceKey = resourceKey;
595 StaticIndices = staticIndices;
596 IndexCount = indexCount;
597 VertexCount = vertexCount;
612 protected class StaticQuadBufferInfo: ResourceBufferInfo
614 public const int IndicesByElement = 6;
616 public const int VertexByElement = 4;
618 private StaticQuadBufferInfo(
string resourceKey,
short[] staticIndices,
int vertexCount)
619 : base(resourceKey, staticIndices, 0, vertexCount)
623 public static StaticQuadBufferInfo
CreateQuadBufferInfo(
string resourceKey,
int maxQuadNumber,
int batchCapacity = 64)
625 var indices =
new short[maxQuadNumber * IndicesByElement];
627 for (var i = 0; i < indices.Length; k += VertexByElement)
629 indices[i++] = (short)(k + 0);
630 indices[i++] = (short)(k + 1);
631 indices[i++] = (short)(k + 2);
632 indices[i++] = (short)(k + 0);
633 indices[i++] = (short)(k + 2);
634 indices[i++] = (short)(k + 3);
637 return new StaticQuadBufferInfo(resourceKey, indices, VertexByElement * maxQuadNumber) { BatchCapacity = batchCapacity };
647 return SpriteTextures[left].Texture0.Id.CompareTo(SpriteTextures[right].Texture0.Id);
651 private class SpriteBackToFrontComparer : QueueComparer<ElementInfo>
653 public override int Compare(
int left,
int right)
655 return ImageInfos[left].Depth.CompareTo(ImageInfos[right].Depth);
659 private class SpriteFrontToBackComparer : QueueComparer<ElementInfo>
661 public override int Compare(
int left,
int right)
663 return ImageInfos[right].Depth.CompareTo(ImageInfos[left].Depth);
667 protected abstract class QueueComparer<TInfo> :
IComparer<int>
671 public abstract int Compare(
int x,
int y);
726 var vertexSize = declaration.CalculateSize();
728 VertexCount = resourceBufferInfo.VertexCount;
729 IndexCount = resourceBufferInfo.IndexCount;
730 IsIndexBufferDynamic = resourceBufferInfo.IsIndexBufferDynamic;
732 VertexBuffer = Buffer.Vertex.New(device, VertexCount * vertexSize, GraphicsResourceUsage.Dynamic).DisposeBy(
this);
734 if (IsIndexBufferDynamic)
736 IndexBuffer = Buffer.Index.New(device, IndexCount * indexStructSize, GraphicsResourceUsage.Dynamic).DisposeBy(
this);
740 IndexBuffer = Buffer.Index.New(device, resourceBufferInfo.StaticIndices).DisposeBy(
this);
741 IndexBuffer.Reload = graphicsResource => ((
Buffer)graphicsResource).Recreate(resourceBufferInfo.StaticIndices);
748 VertexArrayObject = VertexArrayObject.New(device, effect.InputSignature, indexBufferBinding, vertexBufferBinding).DisposeBy(
this);
int IndexCount
The number of indices needed to draw the element.
The layout of a vertex buffer with a set of VertexElement.
DepthStencilState DepthStencilState
int IndexBufferPosition
The current position in index into the index array buffer.
static readonly ParameterKey< Texture > Texture1
DrawTextures[] SpriteTextures
void End()
Flushes the sprite batch and restores the device state to how it was before Begin was called...
Contains depth-stencil state for the device.
int Compare(int left, int right)
virtual void PrepareForRendering()
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
ElementInfo(int vertexCount, int indexCount, ref TDrawInfo drawInfo, float depth=0)
void CheckBeginHasBeenCalled(string functionName)
int StencilReferenceValue
readonly Effect DefaultEffect
Base class for a framework component.
All-in-One Buffer class linked SharpDX.Direct3D11.Buffer.
SiliconStudio.Paradox.Graphics.Buffer Buffer
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
GraphicsDevice GraphicsDevice
bool HasParameter(ParameterKey parameterKey)
readonly int VertexCount
Gets the number of vertices.
SpriteSortMode
Defines sprite sort-rendering options.
readonly Buffer IndexBuffer
The index buffer of the batch.
SamplerState SamplerState
RasterizerState RasterizerState
ResourceBufferInfo(string resourceKey, short[] staticIndices, int indexCount, int vertexCount)
readonly VertexArrayObject VertexArrayObject
The VertexArrayObject of the batch.
BatchBase(GraphicsDevice device, Shaders.EffectBytecode defaultEffectByteCode, ResourceBufferInfo resourceBufferInfo, VertexDeclaration vertexDeclaration, int indexSize=sizeof(short))
void Begin(Effect effect, SpriteSortMode sessionSortMode, BlendState sessionBlendState, SamplerState sessionSamplerState, DepthStencilState sessionDepthStencilState, RasterizerState sessionRasterizerState, int stencilValue)
Begins a sprite batch rendering using the specified sorting mode and blend state, sampler...
static ResourceBufferInfo CreateStaticIndexBufferInfo(string resourceKey, short[] staticIndices, int vertexCount)
Create the buffer resource information for a batch having a dynamic vertex buffer but a static index ...
static StaticQuadBufferInfo CreateQuadBufferInfo(string resourceKey, int maxQuadNumber, int batchCapacity=64)
float Depth
The depth of the element. Used to sort the elements.
bool IsDeferred
Gets a value indicating whether this instance is a deferred graphics device context.
int VertexBufferPosition
The current position in vertex into the vertex array buffer.
static readonly ParameterKey< Texture > Texture0
void CheckEndHasBeenCalled(string functionName)
TDrawInfo DrawInfo
The user draw information.
readonly DeviceResourceContext ResourceContext
readonly Buffer VertexBuffer
The vertex buffer of the batch.
short[] StaticIndices
Gets or sets the static indices to use for the index buffer.
void Draw(Texture texture, Texture texture1, ref ElementInfo elementInfo)
readonly int IndexCount
Gets the number of indices.
static ResourceBufferInfo CreateDynamicIndexBufferInfo(string resourceKey, int indexCount, int vertexCount)
Create the buffer resource information for a batch having both a dynamic index buffer and vertex buff...
bool IsInImmediateMode
Indicate if the batch system is drawing in immediate mode for this buffer.
static bool NotEqual(ref DrawTextures left, ref DrawTextures right)
readonly bool IsIndexBufferDynamic
Gets a boolean indicating if the index buffer is dynamic.
DeviceResourceContext(GraphicsDevice device, Effect effect, VertexDeclaration declaration, ResourceBufferInfo resourceBufferInfo, int indexStructSize)
readonly string ResourceKey
The key used to identify the GPU resource.
Base class for texture resources.
Binding structure that specifies a vertex buffer and other per-vertex parameters (such as offset and ...
int VertexCount
The number of vertex needed to draw the element.