Revision: 2191
Author: mike.popoloski
Date: Mon Mar 19 14:40:29 2012
Log: More work on the SpriteBatch support.
http://code.google.com/p/slimdx/source/detail?r=2191
Added:
/branches/lite/SlimDX.Toolkit/Utilities/AlignedArray.cs
/branches/lite/SlimDX.Toolkit/Utilities/SafeHGlobal.cs
Modified:
/branches/lite/SlimDX.Toolkit/SlimDX.Toolkit.csproj
/branches/lite/SlimDX.Toolkit/Sprites/SpriteBatch.cs
/branches/lite/SlimDX.Toolkit/Sprites/SpriteContextResources.cs
/branches/lite/SlimDX.Toolkit/Sprites/SpriteDeviceResources.cs
/branches/lite/build/SlimDX.vcxproj
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Utilities/AlignedArray.cs Mon Mar 19
14:40:29 2012
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace SlimDX.Toolkit
+{
+ /// <summary>
+ /// Provides a 16-byte aligned array.
+ /// </summary>
+ /// <typeparam name="T">The type of the objects within the array.
These must be value types that only contain other value types; otherwise,
the behavior is undefined.</typeparam>
+ public unsafe class AlignedArray<T> : IDisposable where T : struct
+ {
+ SafeHGlobal memory;
+ byte* ptr;
+
+ /// <summary>
+ /// Gets a pointer to the aligned block of memory.
+ /// </summary>
+ public byte* Pointer
+ {
+ get { return ptr; }
+ }
+
+ /// <summary>
+ /// Gets the number of elements in the array.
+ /// </summary>
+ public int Length
+ {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see
cref="AlignedArray<T>"/> class.
+ /// </summary>
+ /// <param name="count">The number of elements in the
array.</param>
+ /// <exception cref="ArgumentOutOfRangeException">Thrown if
<paramref name="count"/> is not a valid array length.</exception>
+ /// <exception cref="InvalidOperationException">Thrown if an
attempt is made to create an aligned array of reference types.</exception>
+ public AlignedArray(int count)
+ {
+ if (!typeof(T).IsValueType)
+ throw new InvalidOperationException("Cannot create aligned
arrays for reference types.");
+ if (count <= 0)
+ throw new ArgumentOutOfRangeException("count",
count, "Invalid size passed for length of the array.");
+
+ Length = count;
+ memory = new SafeHGlobal(Marshal.SizeOf(typeof(T)) * count +
15);
+ void *mem = memory.DangerousGetHandle().ToPointer();
+
+ // make sure we get a 16-byte aligned pointer
+ ptr = (byte*)(((long)mem + 15) & ~0x0F);
+ }
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing,
releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ if (memory != null)
+ {
+ memory.Close();
+ memory = null;
+ }
+ }
+ }
+}
=======================================
--- /dev/null
+++ /branches/lite/SlimDX.Toolkit/Utilities/SafeHGlobal.cs Mon Mar 19
14:40:29 2012
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace SlimDX.Toolkit
+{
+ /// <summary>
+ /// Provides a safe handle around a block of unmanaged memory.
+ /// </summary>
+ public class SafeHGlobal : SafeHandle
+ {
+ /// <summary>
+ /// When overridden in a derived class, gets a value indicating
whether the handle value is invalid.
+ /// </summary>
+ /// <returns>true if the handle value is invalid; otherwise,
false.</returns>
+ ///
+ /// <PermissionSet>
+ /// <IPermission
class="System.Security.Permissions.SecurityPermission, mscorlib,
Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1" Flags="UnmanagedCode"/>
+ /// </PermissionSet>
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SafeHGlobal"/>
class.
+ /// </summary>
+ /// <param name="sizeInBytes">The size of the block of memory to
allocate, in bytes.</param>
+ public SafeHGlobal(int sizeInBytes)
+ : base(Marshal.AllocHGlobal(sizeInBytes), true)
+ {
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, executes the code required
to free the handle.
+ /// </summary>
+ /// <returns>
+ /// true if the handle is released successfully; otherwise, in the
event of a catastrophic failure, false. In this case, it generates a
releaseHandleFailed MDA Managed Debugging Assistant.
+ /// </returns>
+ protected override bool ReleaseHandle()
+ {
+ Marshal.FreeHGlobal(handle);
+ return true;
+ }
+ }
+}
=======================================
--- /branches/lite/SlimDX.Toolkit/SlimDX.Toolkit.csproj Sun Mar 18 09:50:15
2012
+++ /branches/lite/SlimDX.Toolkit/SlimDX.Toolkit.csproj Mon Mar 19 14:40:29
2012
@@ -21,6 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|
AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -30,10 +31,12 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\SlimDX.Toolkit.XML</DocumentationFile>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
+ <Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -45,6 +48,8 @@
<Compile Include="Sprites\SpriteContextResources.cs" />
<Compile Include="Sprites\SpriteDeviceResources.cs" />
<Compile Include="Sprites\SpriteShaders.cs" />
+ <Compile Include="Utilities\AlignedArray.cs" />
+ <Compile Include="Utilities\SafeHGlobal.cs" />
<Compile Include="Utilities\SharedResourcePool.cs" />
<Compile Include="Sprites\SpriteBatch.cs" />
<Compile Include="VertexTypes\InputElementFactory.cs" />
=======================================
--- /branches/lite/SlimDX.Toolkit/Sprites/SpriteBatch.cs Sat Mar 17
21:28:36 2012
+++ /branches/lite/SlimDX.Toolkit/Sprites/SpriteBatch.cs Mon Mar 19
14:40:29 2012
@@ -4,6 +4,7 @@
using System.Text;
using SlimDX.Direct3D11;
using SlimMath;
+using System.Drawing;
namespace SlimDX.Toolkit
{
@@ -74,10 +75,20 @@
/// and rendered to simultaneously to form several batches that can
then be submitted to the device in the desired
/// order by calling <see cref="SpriteBatch.End"/>.
/// </remarks>
- public class SpriteBatch
- {
+ public class SpriteBatch : IDisposable
+ {
+ const int MaxBatchSize = 2048;
+
+ // these resource pools allow resources to be shared between
sprite batches
+ // the local copies in each sprite batch are wrapped in
ref-counted containers
+ static SharedResourcePool<Device, SpriteDeviceResources>
sharedDeviceResources = new SharedResourcePool<Device,
SpriteDeviceResources>();
+ static SharedResourcePool<DeviceContext, SpriteContextResources>
sharedContextResources = new SharedResourcePool<DeviceContext,
SpriteContextResources>();
+
+ Device device;
bool started;
SpriteSortMode sortMode = SpriteSortMode.Deferred;
+ ISharedResource<SpriteDeviceResources> deviceResources;
+ ISharedResource<SpriteContextResources> contextResources;
/// <summary>
/// The custom blend state to apply to sprites rendered in the
current batch. Set it to <c>null</c> to use the default settings,
@@ -146,13 +157,184 @@
public SpriteBatch(DeviceContext context)
{
TransformMatrix = Matrix.Identity;
+ device = context.Device;
+
+ deviceResources = sharedDeviceResources.DemandCreate(device,
() => new SpriteDeviceResources(device, MaxBatchSize));
+ contextResources =
sharedContextResources.DemandCreate(context, () => new
SpriteContextResources(context, MaxBatchSize));
}
- public void Begin(SpriteSortMode sortMode)
- {
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing,
releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose()
+ {
+ if (deviceResources != null)
+ deviceResources.Dispose();
+ if (contextResources != null)
+ contextResources.Dispose();
+
+ deviceResources = null;
+ contextResources = null;
}
+ /// <summary>
+ /// Begins a batch of sprite drawing operations.
+ /// </summary>
+ /// <param name="sortMode">The sorting mode to use when choosing
when to render sprites.</param>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown if Begin is called more than once without an
intervening End call, or if <see cref="SpriteSortMode.Immediate"/>
+ /// is specified and another SpriteBatch is already doing immediat
rendering.
+ /// </exception>
+ public void Begin(SpriteSortMode sortMode =
SpriteSortMode.Deferred)
+ {
+ if (started)
+ throw new InvalidOperationException("Cannot nest Begin
calls on a single SpriteBatch.");
+
+ this.sortMode = sortMode;
+ if (sortMode == SpriteSortMode.Immediate)
+ {
+ // only one sprite batch can be doing immediate rendering
+ if (contextResources.Resource.InImmediateMode)
+ throw new InvalidOperationException("Only one
SpriteBatch at a time can be using Immediate sorting.");
+
+ PrepareForRendering();
+ contextResources.Resource.InImmediateMode = true;
+ }
+
+ started = true;
+ }
+
+ /// <summary>
+ /// Ends a batch of sprite drawing operations.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown if End is called without first calling Begin, or if End
is called while another SpriteBatch is using immediate rendering.
+ /// </exception>
public void End()
+ {
+ if (!started)
+ throw new InvalidOperationException("Begin must be called
before End.");
+
+ // if we are in immediate mode, the sprites have already been
drawn
+ if (sortMode == SpriteSortMode.Immediate)
+ contextResources.Resource.InImmediateMode = false;
+ else
+ {
+ if (contextResources.Resource.InImmediateMode)
+ throw new InvalidOperationException("Cannot end one
SpriteBatch while another is using immediate rendering.");
+
+ PrepareForRendering();
+ FlushBatch();
+ }
+
+ started = false;
+ }
+
+ #region Draw Overloads
+
+ /// <summary>
+ /// Adds a sprite to the draw queue. Depending on the sorting
mode, the sprite may be rendered immediately or deferred to be sorted and
batched.
+ /// </summary>
+ /// <param name="texture">The sprite's texture.</param>
+ /// <param name="position">The 2D position at which to draw the
sprite.</param>
+ public void Draw(ShaderResourceView texture, Vector2 position)
+ {
+ unsafe { Draw(texture, new Vector4(position, 1.0f, 1.0f),
null, new Color4(1.0f), Vector4.Zero, SpriteEffects.None); }
+ }
+
+ /// <summary>
+ /// Adds a sprite to the draw queue. Depending on the sorting
mode, the sprite may be rendered immediately or deferred to be sorted and
batched.
+ /// </summary>
+ /// <param name="texture">The sprite's texture.</param>
+ /// <param name="position">The 2D position at which to draw the
sprite.</param>
+ /// <param name="color">The color used to tint the sprite.</param>
+ public void Draw(ShaderResourceView texture, Vector2 position,
Color4 color)
+ {
+ unsafe { Draw(texture, new Vector4(position, 1.0f, 1.0f),
null, color, Vector4.Zero, SpriteEffects.None); }
+ }
+
+ /// <summary>
+ /// Adds a sprite to the draw queue. Depending on the sorting
mode, the sprite may be rendered immediately or deferred to be sorted and
batched.
+ /// </summary>
+ /// <param name="texture">The sprite's texture.</param>
+ /// <param name="position">The 2D position at which to draw the
sprite.</param>
+ /// <param name="color">The color used to tint the sprite.</param>
+ /// <param name="sourceRectangle">The portion of the source
texture containing the sprite's image.</param>
+ /// <param name="rotation">An amount to rotate the sprite's image,
in radians.</param>
+ /// <param name="scale">An amount to scale the sprite during
drawing.</param>
+ /// <param name="origin">The origin point for position, scaling,
and rotation effects.</param>
+ /// <param name="effects">Various effects to apply to the rendered
sprite.</param>
+ /// <param name="layerDepth">The layer depth to use for sorting
the sprite.</param>
+ public void Draw(ShaderResourceView texture, Vector2 position,
Color4 color, Rectangle sourceRectangle, float rotation = 0.0f, float scale
= 1.0f, Vector2 origin = new Vector2(), SpriteEffects effects =
SpriteEffects.None, float layerDepth = 0.0f)
+ {
+ unsafe { Draw(texture, new Vector4(position, scale, scale),
&sourceRectangle, color, new Vector4(origin.X, origin.Y, rotation,
layerDepth), effects); }
+ }
+
+ /// <summary>
+ /// Adds a sprite to the draw queue. Depending on the sorting
mode, the sprite may be rendered immediately or deferred to be sorted and
batched.
+ /// </summary>
+ /// <param name="texture">The sprite's texture.</param>
+ /// <param name="position">The 2D position at which to draw the
sprite.</param>
+ /// <param name="color">The color used to tint the sprite.</param>
+ /// <param name="sourceRectangle">The portion of the source
texture containing the sprite's image.</param>
+ /// <param name="scale">An amount to scale the sprite during
drawing.</param>
+ /// <param name="rotation">An amount to rotate the sprite's image,
in radians.</param>
+ /// <param name="origin">The origin point for position, scaling,
and rotation effects.</param>
+ /// <param name="effects">Various effects to apply to the rendered
sprite.</param>
+ /// <param name="layerDepth">The layer depth to use for sorting
the sprite.</param>
+ public void Draw(ShaderResourceView texture, Vector2 position,
Color4 color, Rectangle sourceRectangle, Vector2 scale, float rotation =
0.0f, Vector2 origin = new Vector2(), SpriteEffects effects =
SpriteEffects.None, float layerDepth = 0.0f)
+ {
+ unsafe { Draw(texture, new Vector4(position, scale.X,
scale.Y), &sourceRectangle, color, new Vector4(origin.X, origin.Y,
rotation, layerDepth), effects); }
+ }
+
+ /// <summary>
+ /// Adds a sprite to the draw queue. Depending on the sorting
mode, the sprite may be rendered immediately or deferred to be sorted and
batched.
+ /// </summary>
+ /// <param name="texture">The sprite's texture.</param>
+ /// <param name="destinationRectangle">The destination position
and size.</param>
+ public void Draw(ShaderResourceView texture, Rectangle
destinationRectangle)
+ {
+ unsafe { Draw(texture, new Vector4(destinationRectangle.X,
destinationRectangle.Y, destinationRectangle.Width,
destinationRectangle.Height), null, new Color4(1.0f), Vector4.Zero,
SpriteEffects.None); }
+ }
+
+ /// <summary>
+ /// Adds a sprite to the draw queue. Depending on the sorting
mode, the sprite may be rendered immediately or deferred to be sorted and
batched.
+ /// </summary>
+ /// <param name="texture">The sprite's texture.</param>
+ /// <param name="destinationRectangle">The destination position
and size.</param>
+ /// <param name="color">The color used to tint the sprite.</param>
+ public void Draw(ShaderResourceView texture, Rectangle
destinationRectangle, Color4 color)
+ {
+ unsafe { Draw(texture, new Vector4(destinationRectangle.X,
destinationRectangle.Y, destinationRectangle.Width,
destinationRectangle.Height), null, color, Vector4.Zero,
SpriteEffects.None); }
+ }
+
+ /// <summary>
+ /// Adds a sprite to the draw queue. Depending on the sorting
mode, the sprite may be rendered immediately or deferred to be sorted and
batched.
+ /// </summary>
+ /// <param name="texture">The sprite's texture.</param>
+ /// <param name="destinationRectangle">The destination position
and size.</param>
+ /// <param name="sourceRectangle">The portion of the source
texture containing the sprite's image.</param>
+ /// <param name="color">The color used to tint the sprite.</param>
+ /// <param name="rotation">An amount to rotate the sprite's image,
in radians.</param>
+ /// <param name="origin">The origin point for position, scaling,
and rotation effects.</param>
+ /// <param name="effects">Various effects to apply to the rendered
sprite.</param>
+ /// <param name="layerDepth">The layer depth to use for sorting
the sprite.</param>
+ public void Draw(ShaderResourceView texture, Rectangle
destinationRectangle, Rectangle sourceRectangle, Color4 color, float
rotation = 0.0f, Vector2 origin = new Vector2(), SpriteEffects effects =
SpriteEffects.None, float layerDepth = 0.0f)
+ {
+ unsafe { Draw(texture, new Vector4(destinationRectangle.X,
destinationRectangle.Y, destinationRectangle.Width,
destinationRectangle.Height), &sourceRectangle, color, new
Vector4(origin.X, origin.Y, rotation, layerDepth), effects); }
+ }
+
+ #endregion
+
+ unsafe void Draw(ShaderResourceView texture, Vector4 destination,
Rectangle* source, Color4 color, Vector4 originRotationDepth, SpriteEffects
flags)
+ {
+ }
+
+ void PrepareForRendering()
+ {
+ }
+
+ void FlushBatch()
{
}
}
=======================================
--- /branches/lite/SlimDX.Toolkit/Sprites/SpriteContextResources.cs Sat Mar
17 21:28:36 2012
+++ /branches/lite/SlimDX.Toolkit/Sprites/SpriteContextResources.cs Mon Mar
19 14:40:29 2012
@@ -1,11 +1,46 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using SlimDX.Direct3D11;
+using SlimDX.Toolkit.VertexTypes;
+using SlimMath;
+using Buffer = SlimDX.Direct3D11.Buffer;
namespace SlimDX.Toolkit
{
- class SpriteContextResources
- {
+ // contains shared resources that are associated with a particular
device context
+ class SpriteContextResources : IDisposable
+ {
+ const int VerticesPerSprite = 4;
+
+ public Buffer VertexBuffer;
+ public Buffer ConstantBuffer;
+ public bool InImmediateMode;
+ public int VertexBufferPosition;
+
+ public SpriteContextResources(DeviceContext context, int
maxBatchSize)
+ {
+ // create a dynamic write-only constant buffer for the sprite
transform
+ ConstantBuffer = new Buffer(context.Device, new
BufferDescription
+ {
+ SizeInBytes = Matrix.SizeInBytes,
+ Usage = ResourceUsage.Dynamic,
+ BindFlags = BindFlags.ConstantBuffer,
+ CpuAccessFlags = CpuAccessFlags.Write
+ });
+
+ // create a dynamic write-only vertex buffer for the sprite
vertices, limited by maxBatchSize
+ VertexBuffer = new Buffer(context.Device, new BufferDescription
+ {
+ SizeInBytes = VertexPositionColorTexture.SizeInBytes *
maxBatchSize * VerticesPerSprite,
+ BindFlags = BindFlags.VertexBuffer,
+ Usage = ResourceUsage.Dynamic,
+ CpuAccessFlags = CpuAccessFlags.Write
+ });
+ }
+
+ public void Dispose()
+ {
+ VertexBuffer.Dispose();
+ ConstantBuffer.Dispose();
+ }
}
}
=======================================
--- /branches/lite/SlimDX.Toolkit/Sprites/SpriteDeviceResources.cs Sat Mar
17 21:28:36 2012
+++ /branches/lite/SlimDX.Toolkit/Sprites/SpriteDeviceResources.cs Mon Mar
19 14:40:29 2012
@@ -1,30 +1,65 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using SlimDX.D3DCompiler;
using SlimDX.Direct3D11;
+using SlimDX.Toolkit.VertexTypes;
using Buffer = SlimDX.Direct3D11.Buffer;
-using SlimDX.D3DCompiler;
namespace SlimDX.Toolkit
{
+ // contains shared resources that are associated with a particular
device
class SpriteDeviceResources : IDisposable
{
+ const int IndicesPerSprite = 6;
+ const int VerticesPerSprite = 4;
+
public VertexShader VertexShader;
public PixelShader PixelShader;
public InputLayout InputLayout;
public Buffer IndexBuffer;
- public SpriteDeviceResources(Device device)
- {
+ public SpriteDeviceResources(Device device, int maxBatchSize)
+ {
+ // create the vertex shader and input layout
using (var bytecode =
ShaderBytecode.Compile(SpriteShaders.VertexShader, "VS",
device.VertexShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
{
VertexShader = new VertexShader(device, bytecode);
- }
+ InputLayout = new InputLayout(device, bytecode,
InputElementFactory.DemandCreate(typeof(VertexPositionColorTexture)));
+ }
+
+ // create the pixel shader
+ using (var bytecode =
ShaderBytecode.Compile(SpriteShaders.PixelShader, "PS",
device.PixelShaderProfile, ShaderFlags.OptimizationLevel3,
EffectFlags.None))
+ PixelShader = new PixelShader(device, bytecode);
+
+ // generate indices for simple quads
+ int size = maxBatchSize * IndicesPerSprite * sizeof(short);
+ var indices = new DataStream(size, true, true);
+ for (short i = 0; i < maxBatchSize * VerticesPerSprite; i +=
VerticesPerSprite)
+ {
+ indices.Write(i);
+ indices.Write(i + 1);
+ indices.Write(i + 2);
+
+ indices.Write(i + 1);
+ indices.Write(i + 3);
+ indices.Write(i + 2);
+ }
+
+ // create the index buffer
+ indices.Position = 0;
+ IndexBuffer = new Buffer(device, indices, new BufferDescription
+ {
+ SizeInBytes = size,
+ BindFlags = BindFlags.IndexBuffer,
+ Usage = ResourceUsage.Default
+ });
}
public void Dispose()
{
+ VertexShader.Dispose();
+ PixelShader.Dispose();
+ InputLayout.Dispose();
+ IndexBuffer.Dispose();
}
}
}
=======================================
--- /branches/lite/build/SlimDX.vcxproj Sat Mar 17 21:28:36 2012
+++ /branches/lite/build/SlimDX.vcxproj Mon Mar 19 14:40:29 2012
@@ -212,6 +212,8 @@
<KeyFile>
</KeyFile>
<CLRUnmanagedCodeCheck>true</CLRUnmanagedCodeCheck>
+ <IgnoreAllDefaultLibraries>
+ </IgnoreAllDefaultLibraries>
</Link>
</ItemDefinitionGroup>
<ItemGroup>