VeldridSpriteBatch.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Numerics;
  5. using System.Runtime.CompilerServices;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using Veldrid.SPIRV;
  10. namespace Veldrid.Common
  11. {
  12. public class VeldridSpriteBatch : SpriteBatch<TextureWrapper>
  13. {
  14. private readonly GraphicsDevice _device;
  15. private readonly DeviceBuffer _vertexBuffer;
  16. private readonly Sampler _sampler;
  17. private Pipeline _pipeline;
  18. private readonly ResourceLayout[] _resourceLayouts;
  19. private Dictionary<Texture, (DeviceBuffer buffer, ResourceSet set, ResourceSet textureSet)> _buffers;
  20. public ResourceLayout[] ResourceLayouts => _resourceLayouts;
  21. /// <summary>
  22. /// Creates a new instance of <see cref="VeldridSpriteBatch"/>.
  23. /// </summary>
  24. /// <param name="device">The graphics device to create resources.</param>
  25. /// <param name="outputDescription">The output description of target framebuffer.</param>
  26. /// <param name="shaders">The shaders to use to render. Uses <seealso cref="LoadDefaultShaders(GraphicsDevice)"/> for default.</param>
  27. /// <param name="sampler">The samppler used to sample.</param>
  28. /// <param name="cullMode">Controls which face will be culled. By default the sprite are rendered with forward normal, negatives scales can flips that normal.</param>
  29. /// <param name="blendState">The blend state description for creating the pipeline.</param>
  30. /// <param name="depthStencil">The depth stencil state description for creating the pipeline.</param>
  31. public VeldridSpriteBatch(GraphicsDevice device,
  32. OutputDescription outputDescription,
  33. Shader[] shaders,
  34. Sampler? sampler = null,
  35. FaceCullMode cullMode = FaceCullMode.None,
  36. BlendStateDescription? blendState = null,
  37. DepthStencilStateDescription? depthStencil = null) : base()
  38. {
  39. _sampler = sampler ?? device.Aniso4xSampler;
  40. _device = device;
  41. _vertexBuffer = CreateVertexBuffer(device);
  42. _resourceLayouts = CreateResourceLayouts(device);
  43. _buffers = new Dictionary<Texture, (DeviceBuffer buffer, ResourceSet set, ResourceSet textureSet)>();
  44. var bs = blendState ?? BlendStateDescription.SingleAlphaBlend;
  45. var ds = depthStencil ?? DepthStencilStateDescription.DepthOnlyLessEqual;
  46. _pipeline = CreatePipeline(device, outputDescription, cullMode, bs, ds, shaders, _resourceLayouts);
  47. }
  48. /// <summary>
  49. /// Draw this branch into a <see cref="CommandList"/>.
  50. /// Call this after calling <see cref="SpriteBatch{TTexture}.End"/>
  51. /// </summary>
  52. /// <param name="commandList"></param>
  53. public unsafe void DrawBatch(CommandList commandList)
  54. {
  55. DrawBatch(commandList, _pipeline);
  56. }
  57. public unsafe void DrawBatch(CommandList commandList,Pipeline pipeline)
  58. {
  59. var matrixSize = (int)Marshal.SizeOf<Matrix4x4>();
  60. commandList.SetPipeline(pipeline);
  61. foreach (var item in _batcher)
  62. {
  63. var texture = item.Key;
  64. var group = item.Value;
  65. var pair = GetBuffer(texture, group.Count + matrixSize);
  66. if (pair.buffer.IsDisposed) continue;
  67. var val = group.GetSpan();
  68. var mapped = _device.Map(pair.buffer, MapMode.Write);
  69. Unsafe.Write(mapped.Data.ToPointer(), ViewMatrix);
  70. fixed (void* ptr = &val[0])
  71. {
  72. Buffer.MemoryCopy(ptr, (byte*)mapped.Data.ToPointer() + matrixSize, val.Length * Marshal.SizeOf<BatchItem>(), val.Length * Marshal.SizeOf<BatchItem>());
  73. }
  74. if (pair.buffer.IsDisposed) continue;
  75. _device.Unmap(pair.buffer);
  76. commandList.SetVertexBuffer(0, _vertexBuffer);
  77. commandList.SetGraphicsResourceSet(0, pair.set);
  78. commandList.SetGraphicsResourceSet(1, pair.textureSet);
  79. commandList.Draw(4, (uint)group.Count, 0, 0);
  80. }
  81. }
  82. internal (DeviceBuffer buffer, ResourceSet set, ResourceSet textureSet) GetBuffer(Texture t, int count)
  83. {
  84. var structSize = (uint)Marshal.SizeOf<BatchItem>();
  85. var size = (uint)(Math.Ceiling(count/128f+1)*256 * structSize);
  86. var bci = new BufferDescription((uint)(size), BufferUsage.StructuredBufferReadOnly | BufferUsage.Dynamic, structSize);
  87. if (!_buffers.TryGetValue(t, out var pair))
  88. {
  89. pair.buffer = _device.ResourceFactory.CreateBuffer(bci);
  90. pair.set = _device.ResourceFactory.CreateResourceSet(new ResourceSetDescription(_resourceLayouts[0], pair.buffer));
  91. pair.textureSet = _device.ResourceFactory.CreateResourceSet(new ResourceSetDescription(_resourceLayouts[1], t, _sampler));
  92. _buffers[t] = pair;
  93. }
  94. else if (size > pair.buffer.SizeInBytes)
  95. {
  96. pair.set.Dispose();
  97. pair.buffer.Dispose();
  98. pair.buffer = _device.ResourceFactory.CreateBuffer(bci);
  99. pair.set = _device.ResourceFactory.CreateResourceSet(new ResourceSetDescription(_resourceLayouts[0], pair.buffer));
  100. pair.textureSet = _device.ResourceFactory.CreateResourceSet(new ResourceSetDescription(_resourceLayouts[1], t, _sampler));
  101. _buffers[t] = pair;
  102. }
  103. return pair;
  104. }
  105. /// <inheritdoc/>
  106. protected override void Dispose(bool disposing)
  107. {
  108. if (disposing)
  109. {
  110. _vertexBuffer.Dispose();
  111. _pipeline?.Dispose();
  112. for(int i=0;i<_resourceLayouts.Length;i++)
  113. {
  114. _resourceLayouts[i]?.Dispose();
  115. }
  116. _buffers.Keys.ToList().ForEach(x => x?.Dispose());
  117. _buffers.Values.ToList().ForEach(x =>
  118. {
  119. x.textureSet?.Dispose();
  120. x.buffer?.Dispose();
  121. x.set?.Dispose();
  122. });
  123. }
  124. }
  125. private static DeviceBuffer CreateVertexBuffer(GraphicsDevice device)
  126. {
  127. var bd = new BufferDescription()
  128. {
  129. SizeInBytes = 4 * (uint)Marshal.SizeOf<Vector2>(),
  130. Usage = BufferUsage.VertexBuffer,
  131. };
  132. var buffer = device.ResourceFactory.CreateBuffer(bd);
  133. var vertices = new Vector2[]
  134. {
  135. new Vector2( 0, 0),
  136. new Vector2( 1, 0),
  137. new Vector2( 0, 1),
  138. new Vector2( 1, 1)
  139. };
  140. device.UpdateBuffer(buffer, 0, vertices);
  141. return buffer;
  142. }
  143. private static ResourceLayout[] CreateResourceLayouts(GraphicsDevice device)
  144. {
  145. var layouts = new ResourceLayout[2];
  146. var elements = new ResourceLayoutElementDescription[]
  147. {
  148. new ResourceLayoutElementDescription("Items", ResourceKind.StructuredBufferReadOnly, ShaderStages.Vertex),
  149. };
  150. var rld = new ResourceLayoutDescription(elements);
  151. layouts[0] = device.ResourceFactory.CreateResourceLayout(rld);
  152. elements = new ResourceLayoutElementDescription[]
  153. {
  154. new ResourceLayoutElementDescription("Tex", ResourceKind.TextureReadOnly, ShaderStages.Fragment),
  155. new ResourceLayoutElementDescription("Sampler", ResourceKind.Sampler, ShaderStages.Fragment)
  156. };
  157. rld = new ResourceLayoutDescription(elements);
  158. layouts[1] = device.ResourceFactory.CreateResourceLayout(rld);
  159. return layouts;
  160. }
  161. private static Pipeline CreatePipeline(GraphicsDevice device,
  162. OutputDescription outputDescription,
  163. FaceCullMode cullMode,
  164. BlendStateDescription blendState,
  165. DepthStencilStateDescription depthStencil,
  166. Shader[] shaders,
  167. params ResourceLayout[] layouts)
  168. {
  169. var vertexLayout = new VertexLayoutDescription(
  170. new VertexElementDescription("Position", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2));
  171. var pipelineDescription = new GraphicsPipelineDescription
  172. {
  173. BlendState = blendState,
  174. DepthStencilState = depthStencil,
  175. RasterizerState = RasterizerStateDescription.Default,
  176. PrimitiveTopology = PrimitiveTopology.TriangleStrip,
  177. ResourceLayouts = layouts,
  178. ShaderSet = new ShaderSetDescription(
  179. vertexLayouts: new[] { vertexLayout },
  180. shaders: shaders,
  181. specializations: new[] { new SpecializationConstant(0, device.IsClipSpaceYInverted) }),
  182. Outputs = outputDescription,
  183. };
  184. return device.ResourceFactory.CreateGraphicsPipeline(pipelineDescription);
  185. }
  186. }
  187. }