forked from MoonsideGames/MoonWorks
Window API revision + Framerate limiter (#27)
- add `Backend` enum - `GraphicsDevice` can now take a preferred backend at creation - add `GraphicsDevice.ClaimWindow` - add `GraphicsDevice.UnclaimWindow` - add `GraphicsDevice.SetPresentMode` - add `Window.Claimed` - add `Window.SwapchainFormat` - fix certain odd behaviors in multi-window scenarios - rename `FramerateSettings` to `FrameLimiterSettings` - add `Game.SetFrameLimiter` Reviewed-on: MoonsideGames/MoonWorks#27custom_video_shaders
parent
3ffdf8a929
commit
07c0b1b9a2
|
@ -1 +1 @@
|
|||
Subproject commit 356f8e9ec2a6118b75e32d2a2ed7dbf4297aba78
|
||||
Subproject commit 330896a7be6db93b17b3b47734e449817bb30b7a
|
|
@ -0,0 +1,22 @@
|
|||
namespace MoonWorks
|
||||
{
|
||||
public enum FrameLimiterMode
|
||||
{
|
||||
Uncapped,
|
||||
Capped
|
||||
}
|
||||
|
||||
public struct FrameLimiterSettings
|
||||
{
|
||||
public FrameLimiterMode Mode;
|
||||
public int Cap;
|
||||
|
||||
public FrameLimiterSettings(
|
||||
FrameLimiterMode mode,
|
||||
int cap
|
||||
) {
|
||||
Mode = mode;
|
||||
Cap = cap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
namespace MoonWorks
|
||||
{
|
||||
public enum FramerateMode
|
||||
{
|
||||
Uncapped,
|
||||
Capped
|
||||
}
|
||||
|
||||
public struct FramerateSettings
|
||||
{
|
||||
public FramerateMode Mode;
|
||||
public int Cap;
|
||||
}
|
||||
}
|
55
src/Game.cs
55
src/Game.cs
|
@ -29,23 +29,16 @@ namespace MoonWorks
|
|||
private bool FramerateCapped = false;
|
||||
private TimeSpan FramerateCapTimeSpan = TimeSpan.Zero;
|
||||
|
||||
public Window Window { get; }
|
||||
public GraphicsDevice GraphicsDevice { get; }
|
||||
public AudioDevice AudioDevice { get; }
|
||||
public Inputs Inputs { get; }
|
||||
|
||||
private Dictionary<PresentMode, RefreshCS.Refresh.PresentMode> moonWorksToRefreshPresentMode = new Dictionary<PresentMode, RefreshCS.Refresh.PresentMode>
|
||||
{
|
||||
{ PresentMode.Immediate, RefreshCS.Refresh.PresentMode.Immediate },
|
||||
{ PresentMode.Mailbox, RefreshCS.Refresh.PresentMode.Mailbox },
|
||||
{ PresentMode.FIFO, RefreshCS.Refresh.PresentMode.FIFO },
|
||||
{ PresentMode.FIFORelaxed, RefreshCS.Refresh.PresentMode.FIFORelaxed }
|
||||
};
|
||||
public Window MainWindow { get; }
|
||||
|
||||
public Game(
|
||||
WindowCreateInfo windowCreateInfo,
|
||||
PresentMode presentMode,
|
||||
FramerateSettings framerateSettings,
|
||||
FrameLimiterSettings frameLimiterSettings,
|
||||
int targetTimestep = 60,
|
||||
bool debugMode = false
|
||||
)
|
||||
|
@ -53,12 +46,7 @@ namespace MoonWorks
|
|||
Timestep = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / targetTimestep);
|
||||
gameTimer = Stopwatch.StartNew();
|
||||
|
||||
FramerateCapped = framerateSettings.Mode == FramerateMode.Capped;
|
||||
|
||||
if (FramerateCapped)
|
||||
{
|
||||
FramerateCapTimeSpan = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / framerateSettings.Cap);
|
||||
}
|
||||
SetFrameLimiter(frameLimiterSettings);
|
||||
|
||||
for (int i = 0; i < previousSleepTimes.Length; i += 1)
|
||||
{
|
||||
|
@ -75,14 +63,18 @@ namespace MoonWorks
|
|||
|
||||
Inputs = new Inputs();
|
||||
|
||||
Window = new Window(windowCreateInfo);
|
||||
|
||||
GraphicsDevice = new GraphicsDevice(
|
||||
Window.Handle,
|
||||
moonWorksToRefreshPresentMode[presentMode],
|
||||
Backend.Vulkan,
|
||||
debugMode
|
||||
);
|
||||
|
||||
MainWindow = new Window(windowCreateInfo, GraphicsDevice.WindowFlags);
|
||||
|
||||
if (!GraphicsDevice.ClaimWindow(MainWindow, presentMode))
|
||||
{
|
||||
throw new System.SystemException("Could not claim window!");
|
||||
}
|
||||
|
||||
AudioDevice = new AudioDevice();
|
||||
}
|
||||
|
||||
|
@ -96,12 +88,26 @@ namespace MoonWorks
|
|||
Destroy();
|
||||
|
||||
AudioDevice.Dispose();
|
||||
MainWindow.Dispose();
|
||||
GraphicsDevice.Dispose();
|
||||
Window.Dispose();
|
||||
|
||||
SDL.SDL_Quit();
|
||||
}
|
||||
|
||||
public void SetFrameLimiter(FrameLimiterSettings settings)
|
||||
{
|
||||
FramerateCapped = settings.Mode == FrameLimiterMode.Capped;
|
||||
|
||||
if (FramerateCapped)
|
||||
{
|
||||
FramerateCapTimeSpan = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / settings.Cap);
|
||||
}
|
||||
else
|
||||
{
|
||||
FramerateCapTimeSpan = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void Update(TimeSpan delta);
|
||||
protected abstract void Draw(double alpha);
|
||||
protected virtual void Destroy() {}
|
||||
|
@ -224,7 +230,14 @@ namespace MoonWorks
|
|||
{
|
||||
if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED)
|
||||
{
|
||||
Window.SizeChanged((uint) evt.window.data1, (uint) evt.window.data2);
|
||||
var window = Window.Lookup(evt.window.windowID);
|
||||
window.SizeChanged((uint) evt.window.data1, (uint) evt.window.data2);
|
||||
}
|
||||
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE)
|
||||
{
|
||||
var window = Window.Lookup(evt.window.windowID);
|
||||
GraphicsDevice.UnclaimWindow(window);
|
||||
window.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,15 +13,18 @@ namespace MoonWorks.Graphics
|
|||
public IntPtr Handle { get; }
|
||||
|
||||
// some state for debug validation
|
||||
GraphicsPipeline currentGraphicsPipeline = null;
|
||||
ComputePipeline currentComputePipeline = null;
|
||||
bool renderPassActive = false;
|
||||
GraphicsPipeline currentGraphicsPipeline;
|
||||
ComputePipeline currentComputePipeline;
|
||||
bool renderPassActive;
|
||||
|
||||
// called from RefreshDevice
|
||||
internal CommandBuffer(GraphicsDevice device, IntPtr handle)
|
||||
{
|
||||
Device = device;
|
||||
Handle = handle;
|
||||
currentGraphicsPipeline = null;
|
||||
currentComputePipeline = null;
|
||||
renderPassActive = false;
|
||||
}
|
||||
|
||||
// FIXME: we can probably use the NativeMemory functions to not have to generate arrays here
|
||||
|
@ -815,7 +818,7 @@ namespace MoonWorks.Graphics
|
|||
return new Texture(
|
||||
Device,
|
||||
texturePtr,
|
||||
Device.GetSwapchainFormat(window),
|
||||
window.SwapchainFormat,
|
||||
width,
|
||||
height
|
||||
);
|
||||
|
|
|
@ -8,6 +8,10 @@ namespace MoonWorks.Graphics
|
|||
public class GraphicsDevice : IDisposable
|
||||
{
|
||||
public IntPtr Handle { get; }
|
||||
public Backend Backend { get; }
|
||||
|
||||
private uint windowFlags;
|
||||
public SDL2.SDL.SDL_WindowFlags WindowFlags => (SDL2.SDL.SDL_WindowFlags) windowFlags;
|
||||
|
||||
// Built-in video pipeline
|
||||
private ShaderModule VideoVertexShader { get; }
|
||||
|
@ -19,19 +23,13 @@ namespace MoonWorks.Graphics
|
|||
private readonly List<WeakReference<GraphicsResource>> resources = new List<WeakReference<GraphicsResource>>();
|
||||
|
||||
public GraphicsDevice(
|
||||
IntPtr deviceWindowHandle,
|
||||
Refresh.PresentMode presentMode,
|
||||
Backend preferredBackend,
|
||||
bool debugMode
|
||||
)
|
||||
{
|
||||
var presentationParameters = new Refresh.PresentationParameters
|
||||
{
|
||||
deviceWindowHandle = deviceWindowHandle,
|
||||
presentMode = presentMode
|
||||
};
|
||||
Backend = (Backend) Refresh.Refresh_SelectBackend((Refresh.Backend) preferredBackend, out windowFlags);
|
||||
|
||||
Handle = Refresh.Refresh_CreateDevice(
|
||||
presentationParameters,
|
||||
Conversions.BoolToByte(debugMode)
|
||||
);
|
||||
|
||||
|
@ -56,6 +54,43 @@ namespace MoonWorks.Graphics
|
|||
);
|
||||
}
|
||||
|
||||
public bool ClaimWindow(Window window, PresentMode presentMode)
|
||||
{
|
||||
var success = Conversions.ByteToBool(
|
||||
Refresh.Refresh_ClaimWindow(
|
||||
Handle,
|
||||
window.Handle,
|
||||
(Refresh.PresentMode) presentMode
|
||||
)
|
||||
);
|
||||
|
||||
if (success)
|
||||
{
|
||||
window.Claimed = true;
|
||||
window.SwapchainFormat = GetSwapchainFormat(window);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void UnclaimWindow(Window window)
|
||||
{
|
||||
Refresh.Refresh_UnclaimWindow(
|
||||
Handle,
|
||||
window.Handle
|
||||
);
|
||||
window.Claimed = false;
|
||||
}
|
||||
|
||||
public void SetPresentMode(Window window, PresentMode presentMode)
|
||||
{
|
||||
Refresh.Refresh_SetSwapchainPresentMode(
|
||||
Handle,
|
||||
window.Handle,
|
||||
(Refresh.PresentMode) presentMode
|
||||
);
|
||||
}
|
||||
|
||||
public CommandBuffer AcquireCommandBuffer()
|
||||
{
|
||||
return new CommandBuffer(this, Refresh.Refresh_AcquireCommandBuffer(Handle, 0));
|
||||
|
@ -82,7 +117,7 @@ namespace MoonWorks.Graphics
|
|||
Refresh.Refresh_Wait(Handle);
|
||||
}
|
||||
|
||||
public TextureFormat GetSwapchainFormat(Window window)
|
||||
private TextureFormat GetSwapchainFormat(Window window)
|
||||
{
|
||||
return (TextureFormat) Refresh.Refresh_GetSwapchainFormat(Handle, window.Handle);
|
||||
}
|
||||
|
|
|
@ -280,4 +280,12 @@ namespace MoonWorks.Graphics
|
|||
FloatOpaqueWhite,
|
||||
IntOpaqueWhite
|
||||
}
|
||||
|
||||
public enum Backend
|
||||
{
|
||||
DontCare,
|
||||
Vulkan,
|
||||
PS5,
|
||||
Invalid
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SDL2;
|
||||
|
||||
namespace MoonWorks
|
||||
|
@ -10,29 +11,32 @@ namespace MoonWorks
|
|||
public uint Width { get; private set; }
|
||||
public uint Height { get; private set; }
|
||||
|
||||
public bool Claimed { get; internal set; }
|
||||
public MoonWorks.Graphics.TextureFormat SwapchainFormat { get; internal set; }
|
||||
|
||||
private bool IsDisposed;
|
||||
|
||||
public Window(WindowCreateInfo windowCreateInfo)
|
||||
{
|
||||
var windowFlags = SDL.SDL_WindowFlags.SDL_WINDOW_VULKAN;
|
||||
private static Dictionary<uint, Window> idLookup = new Dictionary<uint, Window>();
|
||||
|
||||
public Window(WindowCreateInfo windowCreateInfo, SDL.SDL_WindowFlags flags)
|
||||
{
|
||||
if (windowCreateInfo.ScreenMode == ScreenMode.Fullscreen)
|
||||
{
|
||||
windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN;
|
||||
}
|
||||
else if (windowCreateInfo.ScreenMode == ScreenMode.BorderlessWindow)
|
||||
{
|
||||
windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
}
|
||||
|
||||
if (windowCreateInfo.SystemResizable)
|
||||
{
|
||||
windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE;
|
||||
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
|
||||
if (windowCreateInfo.StartMaximized)
|
||||
{
|
||||
windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_MAXIMIZED;
|
||||
flags |= SDL.SDL_WindowFlags.SDL_WINDOW_MAXIMIZED;
|
||||
}
|
||||
|
||||
ScreenMode = windowCreateInfo.ScreenMode;
|
||||
|
@ -43,11 +47,13 @@ namespace MoonWorks
|
|||
SDL.SDL_WINDOWPOS_UNDEFINED,
|
||||
(int) windowCreateInfo.WindowWidth,
|
||||
(int) windowCreateInfo.WindowHeight,
|
||||
windowFlags
|
||||
flags
|
||||
);
|
||||
|
||||
Width = windowCreateInfo.WindowWidth;
|
||||
Height = windowCreateInfo.WindowHeight;
|
||||
|
||||
idLookup.Add(SDL.SDL_GetWindowID(Handle), this);
|
||||
}
|
||||
|
||||
public void ChangeScreenMode(ScreenMode screenMode)
|
||||
|
@ -81,6 +87,11 @@ namespace MoonWorks
|
|||
Height = height;
|
||||
}
|
||||
|
||||
internal static Window Lookup(uint windowID)
|
||||
{
|
||||
return idLookup.ContainsKey(windowID) ? idLookup[windowID] : null;
|
||||
}
|
||||
|
||||
internal void SizeChanged(uint width, uint height)
|
||||
{
|
||||
Width = width;
|
||||
|
@ -96,6 +107,7 @@ namespace MoonWorks
|
|||
// dispose managed state (managed objects)
|
||||
}
|
||||
|
||||
idLookup.Remove(SDL.SDL_GetWindowID(Handle));
|
||||
SDL.SDL_DestroyWindow(Handle);
|
||||
|
||||
IsDisposed = true;
|
||||
|
|
Loading…
Reference in New Issue