|
2 | 2 | using System.Numerics; |
3 | 3 | using System.Runtime.InteropServices; |
4 | 4 | using SDL2; |
| 5 | +using Serilog; |
5 | 6 |
|
6 | 7 | namespace Yafc.UI { |
7 | 8 | // Main window is resizable and hardware-accelerated |
@@ -63,14 +64,75 @@ protected WindowMain(Padding padding) : base(padding) { } |
63 | 64 | } |
64 | 65 |
|
65 | 66 | internal class MainWindowDrawingSurface : DrawingSurface { |
| 67 | + private static readonly ILogger logger = Logging.GetLogger<MainWindowDrawingSurface>(); |
66 | 68 | private readonly IconAtlas atlas = new IconAtlas(); |
67 | 69 | private readonly IntPtr circleTexture; |
68 | 70 |
|
69 | 71 | public override Window window { get; } |
70 | 72 |
|
| 73 | + /// <summary> |
| 74 | + /// Function <c>PickRenderDriver()</c> picks the best rendering backend available on the platform. |
| 75 | + /// |
| 76 | + /// This seems like something that SDL2 should do on its own, since the point of SDL2 is to abstract across platform render |
| 77 | + /// APIs, and it sort of does - but SDL2 has been around for a really long time and its defaults reflect that. On Windows, |
| 78 | + /// it will autoselect DirectX/Direct3D 9 if given half a chance; DX9 was of course the version that shipped in 2002 and |
| 79 | + /// supported Windows 98. This is despite the fact that SDL2 supports DX12 and DX11 where possible, and in 2024 "where possible" |
| 80 | + /// is really going to be "everywhere" - it just doesn't seem to default select them. |
| 81 | + /// |
| 82 | + /// Instead, we can specify the render driver to use when building a renderer instance; to figure out which one, you have to |
| 83 | + /// go iterate through all the render drivers the library supports and pick the right one by string comparing its name. |
| 84 | + /// </summary> |
| 85 | + /// <param name="flags"> |
| 86 | + /// The flags you were going to/are about to pass to SDL_CreateRenderer, just to make sure the function doesn't pick something |
| 87 | + /// incompatible (this is paranoia since the major renderers tend to support everything relevant). |
| 88 | + /// </param> |
| 89 | + /// <returns>The index of the selected render driver, including 0 (SDL autoselect) if no known-best driver exists on this machine. |
| 90 | + /// This value should be fed to the second argument of SDL_CreateRenderer()</returns> |
| 91 | + private int PickRenderDriver(SDL.SDL_RendererFlags flags) { |
| 92 | + nint numRenderDrivers = SDL.SDL_GetNumRenderDrivers(); |
| 93 | + logger.Debug($"Render drivers available: {numRenderDrivers}"); |
| 94 | + int selectedRenderDriver = 0; |
| 95 | + for (int thisRenderDriver = 0; thisRenderDriver < numRenderDrivers; thisRenderDriver++) { |
| 96 | + nint res = SDL.SDL_GetRenderDriverInfo(thisRenderDriver, out SDL.SDL_RendererInfo rendererInfo); |
| 97 | + if (res != 0) { |
| 98 | + string reason = SDL.SDL_GetError(); |
| 99 | + logger.Warning($"Render driver {thisRenderDriver} GetInfo failed: {res}: {reason}"); |
| 100 | + continue; |
| 101 | + } |
| 102 | + // This is for some reason the one data structure that the dotnet library doesn't provide a native unmarshal for |
| 103 | + string? driverName = Marshal.PtrToStringAnsi(rendererInfo.name); |
| 104 | + if (driverName is null) { |
| 105 | + logger.Warning($"Render driver {thisRenderDriver} has an empty name, cannot compare, skipping"); |
| 106 | + continue; |
| 107 | + } |
| 108 | + System.Diagnostics.Debug.WriteLine($"Render driver {thisRenderDriver} is {driverName} flags 0x{rendererInfo.flags.ToString("X")}"); |
| 109 | + if ((rendererInfo.flags | (uint)flags) != rendererInfo.flags) { |
| 110 | + logger.Debug($"Render driver {driverName} flags do not cover requested flags {flags}, skipping"); |
| 111 | + continue; |
| 112 | + } |
| 113 | + |
| 114 | + // SDL2 does actually have a fixed (from code) ordering of available render drivers, so doing a full list scan instead of returning |
| 115 | + // immediately is a bit paranoid, but paranoia comes well-recommended when dealing with graphics drivers |
| 116 | + if (driverName == "direct3d12") { |
| 117 | + logger.Debug($"Selecting render driver {thisRenderDriver} (DX12)"); |
| 118 | + selectedRenderDriver = thisRenderDriver; |
| 119 | + } |
| 120 | + else if (driverName == "direct3d11" && selectedRenderDriver == 0) { |
| 121 | + logger.Debug($"Selecting render driver {thisRenderDriver} (DX11)"); |
| 122 | + selectedRenderDriver = thisRenderDriver; |
| 123 | + } |
| 124 | + } |
| 125 | + logger.Debug($"Selected render driver index {selectedRenderDriver}"); |
| 126 | + return selectedRenderDriver; |
| 127 | + } |
| 128 | + |
71 | 129 | public MainWindowDrawingSurface(WindowMain window) : base(window.pixelsPerUnit) { |
72 | 130 | this.window = window; |
73 | | - renderer = SDL.SDL_CreateRenderer(window.window, 0, SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC); |
| 131 | + |
| 132 | + renderer = SDL.SDL_CreateRenderer(window.window, PickRenderDriver(SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC), SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC); |
| 133 | + |
| 134 | + nint result = SDL.SDL_GetRendererInfo(renderer, out SDL.SDL_RendererInfo info); |
| 135 | + logger.Information($"Driver: {SDL.SDL_GetCurrentVideoDriver()} Renderer: {Marshal.PtrToStringAnsi(info.name)}"); |
74 | 136 | circleTexture = SDL.SDL_CreateTextureFromSurface(renderer, RenderingUtils.CircleSurface); |
75 | 137 | byte colorMod = RenderingUtils.darkMode ? (byte)255 : (byte)0; |
76 | 138 | _ = SDL.SDL_SetTextureColorMod(circleTexture, colorMod, colorMod, colorMod); |
|
0 commit comments