Program.Veldrid.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. using System;
  2. using System.Numerics;
  3. using System.Runtime.InteropServices;
  4. using System.Text;
  5. using ImGuiNET;
  6. using OpenTK.Windowing.GraphicsLibraryFramework;
  7. using Veldrid;
  8. using Veldrid.OpenGL;
  9. using Veldrid.SPIRV;
  10. using Veldrid.Vk;
  11. namespace Pow3r
  12. {
  13. internal sealed unsafe partial class Program
  14. {
  15. private const string VDVertexShader = @"
  16. #version 460
  17. layout (location = 0) in vec2 Position;
  18. layout (location = 1) in vec2 UV;
  19. layout (location = 2) in vec4 Color;
  20. layout (set = 0, binding = 0) uniform ProjMtx {
  21. mat4 _ProjMtx;
  22. };
  23. layout (location = 0) out vec2 Frag_UV;
  24. layout (location = 1) out vec4 Frag_Color;
  25. // Converts a color from sRGB gamma to linear light gamma
  26. vec4 toLinear(vec4 sRGB)
  27. {
  28. bvec3 cutoff = lessThan(sRGB.rgb, vec3(0.04045));
  29. vec3 higher = pow((sRGB.rgb + vec3(0.055))/vec3(1.055), vec3(2.4));
  30. vec3 lower = sRGB.rgb/vec3(12.92);
  31. return vec4(mix(higher, lower, cutoff), sRGB.a);
  32. }
  33. void main()
  34. {
  35. Frag_UV = UV;
  36. Frag_Color = toLinear(Color);
  37. gl_Position = _ProjMtx * vec4(Position.xy,0,1);
  38. }";
  39. private const string VDFragmentShader = @"
  40. #version 460
  41. layout (location = 0) in vec2 Frag_UV;
  42. layout (location = 1) in vec4 Frag_Color;
  43. layout (set = 1, binding = 0) uniform texture2D Texture;
  44. layout (set = 1, binding = 1) uniform sampler TextureSampler;
  45. layout (location = 0) out vec4 Out_Color;
  46. void main()
  47. {
  48. Out_Color = Frag_Color * texture(sampler2D(Texture, TextureSampler), Frag_UV.st);
  49. }";
  50. private VeldridRenderer _vdRenderer = VeldridRenderer.Vulkan;
  51. private GraphicsDevice _vdGfxDevice;
  52. private CommandList _vdCommandList;
  53. private Pipeline _vdPipeline;
  54. private Shader[] _vdShaders;
  55. private ResourceSet _vdSetTexture;
  56. private ResourceSet _vdSetProjMatrix;
  57. private Texture _vdTexture;
  58. private Sampler _vdSampler;
  59. private DeviceBuffer _vdProjMatrixUniformBuffer;
  60. private int _vdLastWidth;
  61. private int _vdLastHeight;
  62. private VdFencedDatum[] _fencedData = Array.Empty<VdFencedDatum>();
  63. private void InitVeldrid()
  64. {
  65. var options = new GraphicsDeviceOptions
  66. {
  67. #if DEBUG
  68. Debug = true,
  69. #endif
  70. HasMainSwapchain = true,
  71. SyncToVerticalBlank = _vsync,
  72. PreferStandardClipSpaceYDirection = true,
  73. SwapchainSrgbFormat = true
  74. };
  75. GLFW.GetFramebufferSize(_window.WindowPtr, out var w, out var h);
  76. var hwnd = GLFW.GetWin32Window(_window.WindowPtr);
  77. var hinstance = GetModuleHandleA(null);
  78. switch (_vdRenderer)
  79. {
  80. case VeldridRenderer.Vulkan:
  81. _vdGfxDevice = GraphicsDevice.CreateVulkan(
  82. options,
  83. VkSurfaceSource.CreateWin32((nint) hinstance, hwnd),
  84. (uint) w, (uint) h);
  85. break;
  86. case VeldridRenderer.D3D11:
  87. _vdGfxDevice = GraphicsDevice.CreateD3D11(options, hwnd, (uint) w, (uint) h);
  88. break;
  89. case VeldridRenderer.OpenGL:
  90. {
  91. var platInfo = new OpenGLPlatformInfo(
  92. (nint) _window.WindowPtr,
  93. GLFW.GetProcAddress,
  94. ptr => GLFW.MakeContextCurrent((Window*) ptr),
  95. () => (nint) GLFW.GetCurrentContext(),
  96. () => GLFW.MakeContextCurrent(null),
  97. ptr => GLFW.DestroyWindow((Window*) ptr),
  98. () => GLFW.SwapBuffers(_window.WindowPtr),
  99. vsync => GLFW.SwapInterval(vsync ? 1 : 0));
  100. _vdGfxDevice = GraphicsDevice.CreateOpenGL(options, platInfo, (uint) w, (uint) h);
  101. break;
  102. }
  103. }
  104. var factory = _vdGfxDevice.ResourceFactory;
  105. _vdCommandList = factory.CreateCommandList();
  106. _vdCommandList.Name = "Honk";
  107. var vtxLayout = new VertexLayoutDescription(
  108. new VertexElementDescription("Position", VertexElementFormat.Float2,
  109. VertexElementSemantic.TextureCoordinate),
  110. new VertexElementDescription("UV", VertexElementFormat.Float2, VertexElementSemantic.TextureCoordinate),
  111. new VertexElementDescription("Color", VertexElementFormat.Byte4_Norm,
  112. VertexElementSemantic.TextureCoordinate));
  113. var vtxShaderDesc = new ShaderDescription(
  114. ShaderStages.Vertex,
  115. Encoding.UTF8.GetBytes(VDVertexShader),
  116. "main");
  117. var fragShaderDesc = new ShaderDescription(
  118. ShaderStages.Fragment,
  119. Encoding.UTF8.GetBytes(VDFragmentShader),
  120. "main");
  121. _vdShaders = factory.CreateFromSpirv(vtxShaderDesc, fragShaderDesc);
  122. _vdShaders[0].Name = "VertexShader";
  123. _vdShaders[1].Name = "FragmentShader";
  124. var layoutTexture = factory.CreateResourceLayout(new ResourceLayoutDescription(
  125. new ResourceLayoutElementDescription(
  126. "Texture",
  127. ResourceKind.TextureReadOnly,
  128. ShaderStages.Fragment),
  129. new ResourceLayoutElementDescription(
  130. "TextureSampler",
  131. ResourceKind.Sampler,
  132. ShaderStages.Fragment)));
  133. layoutTexture.Name = "LayoutTexture";
  134. var layoutProjMatrix = factory.CreateResourceLayout(new ResourceLayoutDescription(
  135. new ResourceLayoutElementDescription(
  136. "ProjMtx",
  137. ResourceKind.UniformBuffer,
  138. ShaderStages.Vertex)));
  139. layoutProjMatrix.Name = "LayoutProjMatrix";
  140. var pipelineDesc = new GraphicsPipelineDescription(
  141. new BlendStateDescription(
  142. RgbaFloat.White,
  143. new BlendAttachmentDescription(
  144. true,
  145. BlendFactor.SourceAlpha,
  146. BlendFactor.InverseSourceAlpha,
  147. BlendFunction.Add,
  148. BlendFactor.One,
  149. BlendFactor.InverseSourceAlpha,
  150. BlendFunction.Add)
  151. ),
  152. DepthStencilStateDescription.Disabled,
  153. new RasterizerStateDescription(
  154. FaceCullMode.None,
  155. PolygonFillMode.Solid,
  156. FrontFace.Clockwise,
  157. depthClipEnabled: false,
  158. scissorTestEnabled: true),
  159. PrimitiveTopology.TriangleList,
  160. new ShaderSetDescription(new[] {vtxLayout}, _vdShaders),
  161. new[] {layoutProjMatrix, layoutTexture},
  162. new OutputDescription(
  163. null,
  164. new OutputAttachmentDescription(PixelFormat.B8_G8_R8_A8_UNorm_SRgb))
  165. );
  166. _vdPipeline = factory.CreateGraphicsPipeline(pipelineDesc);
  167. _vdPipeline.Name = "MainPipeline";
  168. _vdProjMatrixUniformBuffer = factory.CreateBuffer(new BufferDescription(
  169. (uint) sizeof(Matrix4x4),
  170. BufferUsage.Dynamic | BufferUsage.UniformBuffer));
  171. _vdProjMatrixUniformBuffer.Name = "_vdProjMatrixUniformBuffer";
  172. _vdSetProjMatrix = factory.CreateResourceSet(new ResourceSetDescription(
  173. layoutProjMatrix,
  174. _vdProjMatrixUniformBuffer));
  175. _vdSetProjMatrix.Name = "_vdSetProjMatrix";
  176. var io = ImGui.GetIO();
  177. io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out var width, out var height, out _);
  178. _vdTexture = factory.CreateTexture(TextureDescription.Texture2D(
  179. (uint) width, (uint) height,
  180. mipLevels: 1,
  181. arrayLayers: 1,
  182. PixelFormat.R8_G8_B8_A8_UNorm_SRgb,
  183. TextureUsage.Sampled));
  184. _vdTexture.Name = "MainTexture";
  185. _vdSampler = factory.CreateSampler(SamplerDescription.Linear);
  186. _vdSampler.Name = "MainSampler";
  187. _vdGfxDevice.UpdateTexture(
  188. _vdTexture,
  189. (IntPtr) pixels,
  190. (uint) (width * height * 4),
  191. x: 0, y: 0, z: 0,
  192. (uint) width, (uint) height, depth: 1,
  193. mipLevel: 0,
  194. arrayLayer: 0);
  195. _vdSetTexture = factory.CreateResourceSet(new ResourceSetDescription(
  196. layoutTexture,
  197. _vdTexture,
  198. _vdSampler));
  199. _vdSetTexture.Name = "SetTexture";
  200. io.Fonts.SetTexID(0);
  201. io.Fonts.ClearTexData();
  202. _vdGfxDevice.ResizeMainWindow((uint) w, (uint) h);
  203. _vdGfxDevice.SwapBuffers();
  204. }
  205. private void RenderVeldrid()
  206. {
  207. GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
  208. if (_vdLastWidth != fbW && _vdLastHeight != fbH)
  209. {
  210. _vdGfxDevice.ResizeMainWindow((uint) fbW, (uint) fbH);
  211. _vdLastWidth = fbW;
  212. _vdLastHeight = fbH;
  213. }
  214. _vdCommandList.Begin();
  215. _vdCommandList.SetFramebuffer(_vdGfxDevice.SwapchainFramebuffer);
  216. _vdCommandList.SetViewport(0, new Viewport(0, 0, fbW, fbH, 0, 1));
  217. _vdCommandList.ClearColorTarget(0, RgbaFloat.Black);
  218. var factory = _vdGfxDevice.ResourceFactory;
  219. var drawData = ImGui.GetDrawData();
  220. ref var fencedData = ref GetFreeFencedData();
  221. ref var vtxBuf = ref fencedData.VertexBuffer;
  222. ref var idxBuf = ref fencedData.IndexBuffer;
  223. var byteLenVtx = (uint) (sizeof(ImDrawVert) * drawData.TotalVtxCount);
  224. if (fencedData.VertexBuffer == null || vtxBuf.SizeInBytes < byteLenVtx)
  225. {
  226. vtxBuf?.Dispose();
  227. vtxBuf = factory.CreateBuffer(new BufferDescription(
  228. byteLenVtx,
  229. BufferUsage.VertexBuffer | BufferUsage.Dynamic));
  230. vtxBuf.Name = "_vdVtxBuffer";
  231. }
  232. var byteLenIdx = (uint) (sizeof(ushort) * drawData.TotalIdxCount);
  233. if (idxBuf == null || idxBuf.SizeInBytes < byteLenIdx)
  234. {
  235. idxBuf?.Dispose();
  236. idxBuf = factory.CreateBuffer(new BufferDescription(
  237. byteLenIdx,
  238. BufferUsage.IndexBuffer | BufferUsage.Dynamic));
  239. idxBuf.Name = "_vdIdxBuffer";
  240. }
  241. var vtxOffset = 0;
  242. var idxOffset = 0;
  243. var mappedVtxBuf = MappedToSpan(_vdGfxDevice.Map<ImDrawVert>(vtxBuf, MapMode.Write));
  244. var mappedIdxBuf = MappedToSpan(_vdGfxDevice.Map<ushort>(idxBuf, MapMode.Write));
  245. var l = drawData.DisplayPos.X;
  246. var r = drawData.DisplayPos.X + drawData.DisplaySize.X;
  247. var t = drawData.DisplayPos.Y;
  248. var b = drawData.DisplayPos.Y + drawData.DisplaySize.Y;
  249. var matrix = Matrix4x4.CreateOrthographicOffCenter(l, r, b, t, -1, 1);
  250. var clipOff = drawData.DisplayPos;
  251. var clipScale = drawData.FramebufferScale;
  252. _vdCommandList.UpdateBuffer(_vdProjMatrixUniformBuffer, 0, ref matrix);
  253. _vdCommandList.SetPipeline(_vdPipeline);
  254. _vdCommandList.SetGraphicsResourceSet(0, _vdSetProjMatrix);
  255. _vdCommandList.SetGraphicsResourceSet(1, _vdSetTexture);
  256. _vdCommandList.SetVertexBuffer(0, vtxBuf);
  257. _vdCommandList.SetIndexBuffer(idxBuf, IndexFormat.UInt16);
  258. for (var n = 0; n < drawData.CmdListsCount; n++)
  259. {
  260. var drawList = drawData.CmdListsRange[n];
  261. var drawVtx = new Span<ImDrawVert>((void*) drawList.VtxBuffer.Data, drawList.VtxBuffer.Size);
  262. var drawIdx = new Span<ushort>((void*) drawList.IdxBuffer.Data, drawList.IdxBuffer.Size);
  263. drawVtx.CopyTo(mappedVtxBuf[vtxOffset..]);
  264. drawIdx.CopyTo(mappedIdxBuf[idxOffset..]);
  265. for (var cmdI = 0; cmdI < drawList.CmdBuffer.Size; cmdI++)
  266. {
  267. var cmd = drawList.CmdBuffer[cmdI];
  268. Vector4 clipRect = default;
  269. clipRect.X = (cmd.ClipRect.X - clipOff.X) * clipScale.X;
  270. clipRect.Y = (cmd.ClipRect.Y - clipOff.Y) * clipScale.Y;
  271. clipRect.Z = (cmd.ClipRect.Z - clipOff.X) * clipScale.X;
  272. clipRect.W = (cmd.ClipRect.W - clipOff.Y) * clipScale.Y;
  273. _vdCommandList.SetScissorRect(
  274. 0,
  275. (uint) clipRect.X,
  276. (uint) clipRect.Y,
  277. (uint) (clipRect.Z - clipRect.X),
  278. (uint) (clipRect.W - clipRect.Y));
  279. _vdCommandList.DrawIndexed(
  280. cmd.ElemCount,
  281. 1,
  282. (uint) (cmd.IdxOffset + idxOffset),
  283. (int) (cmd.VtxOffset + vtxOffset),
  284. 0);
  285. }
  286. vtxOffset += drawVtx.Length;
  287. idxOffset += drawIdx.Length;
  288. }
  289. _vdGfxDevice.Unmap(vtxBuf);
  290. _vdGfxDevice.Unmap(idxBuf);
  291. _vdCommandList.End();
  292. _vdGfxDevice.SubmitCommands(_vdCommandList, fencedData.Fence);
  293. _vdGfxDevice.SwapBuffers();
  294. }
  295. private ref VdFencedDatum GetFreeFencedData()
  296. {
  297. for (var i = 0; i < _fencedData.Length; i++)
  298. {
  299. ref var fenced = ref _fencedData[i];
  300. if (fenced.Fence.Signaled)
  301. {
  302. fenced.Fence.Reset();
  303. return ref fenced;
  304. }
  305. }
  306. Array.Resize(ref _fencedData, _fencedData.Length + 1);
  307. ref var slot = ref _fencedData[^1];
  308. slot = new VdFencedDatum {Fence = _vdGfxDevice.ResourceFactory.CreateFence(false)};
  309. return ref slot;
  310. }
  311. private static Span<T> MappedToSpan<T>(MappedResourceView<T> mapped) where T : struct
  312. {
  313. return MemoryMarshal.CreateSpan(ref mapped[0], mapped.Count);
  314. }
  315. [DllImport("kernel32.dll")]
  316. private static extern void* GetModuleHandleA(byte* lpModuleName);
  317. private struct VdFencedDatum
  318. {
  319. public Fence Fence;
  320. public DeviceBuffer IndexBuffer;
  321. public DeviceBuffer VertexBuffer;
  322. }
  323. private enum VeldridRenderer
  324. {
  325. Vulkan,
  326. D3D11,
  327. OpenGL
  328. }
  329. }
  330. }