MoonWorks/src/Graphics/GraphicsDevice.cs

221 lines
5.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using RefreshCS;
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; }
private ShaderModule VideoFragmentShader { get; }
internal GraphicsPipeline VideoPipeline { get; }
public bool IsDisposed { get; private set; }
private readonly List<WeakReference<GraphicsResource>> resources = new List<WeakReference<GraphicsResource>>();
private static bool usingCustomVideoShaders;
private static string customVideoVertexShaderFilepath;
private static string customVideoFragmentShaderFilepath;
public GraphicsDevice(
Backend preferredBackend,
bool debugMode
)
{
Backend = (Backend) Refresh.Refresh_SelectBackend((Refresh.Backend) preferredBackend, out windowFlags);
if (Backend == Backend.Invalid)
{
throw new System.Exception("Could not set graphics backend!");
}
Handle = Refresh.Refresh_CreateDevice(
Conversions.BoolToByte(debugMode)
);
Stream videoVertexShaderStream;
Stream videoFragmentShaderStream;
if (!usingCustomVideoShaders)
{
videoVertexShaderStream = GetEmbeddedResource("MoonWorks.Shaders.FullscreenVert.spv");
videoFragmentShaderStream = GetEmbeddedResource("MoonWorks.Shaders.YUV2RGBAFrag.spv");
}
else
{
videoVertexShaderStream = File.Open(customVideoVertexShaderFilepath, FileMode.Open, FileAccess.Read);
videoFragmentShaderStream = File.Open(customVideoFragmentShaderFilepath, FileMode.Open, FileAccess.Read);
}
VideoVertexShader = new ShaderModule(this, videoVertexShaderStream);
VideoFragmentShader = new ShaderModule(this, videoFragmentShaderStream);
videoVertexShaderStream.Close();
videoFragmentShaderStream.Close();
VideoPipeline = new GraphicsPipeline(
this,
new GraphicsPipelineCreateInfo
{
AttachmentInfo = new GraphicsPipelineAttachmentInfo(
new ColorAttachmentDescription(TextureFormat.R8G8B8A8, ColorAttachmentBlendState.None)
),
DepthStencilState = DepthStencilState.Disable,
VertexShaderInfo = GraphicsShaderInfo.Create(VideoVertexShader, "main", 0),
FragmentShaderInfo = GraphicsShaderInfo.Create(VideoFragmentShader, "main", 3),
VertexInputState = VertexInputState.Empty,
RasterizerState = RasterizerState.CCW_CullNone,
PrimitiveType = PrimitiveType.TriangleList,
MultisampleState = MultisampleState.None
}
);
}
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));
}
public unsafe void Submit(params CommandBuffer[] commandBuffers)
{
var commandBufferPtrs = stackalloc IntPtr[commandBuffers.Length];
for (var i = 0; i < commandBuffers.Length; i += 1)
{
commandBufferPtrs[i] = commandBuffers[i].Handle;
}
Refresh.Refresh_Submit(
Handle,
(uint) commandBuffers.Length,
(IntPtr) commandBufferPtrs
);
}
public void Wait()
{
Refresh.Refresh_Wait(Handle);
}
private TextureFormat GetSwapchainFormat(Window window)
{
return (TextureFormat) Refresh.Refresh_GetSwapchainFormat(Handle, window.Handle);
}
internal void AddResourceReference(WeakReference<GraphicsResource> resourceReference)
{
lock (resources)
{
resources.Add(resourceReference);
}
}
internal void RemoveResourceReference(WeakReference<GraphicsResource> resourceReference)
{
lock (resources)
{
resources.Remove(resourceReference);
}
}
private static Stream GetEmbeddedResource(string name)
{
return typeof(GraphicsDevice).Assembly.GetManifestResourceStream(name);
}
/// <summary>
/// Use this ONLY for platforms with non-standard graphics APIs where the shader code can't be embedded into the assembly!
/// </summary>
public static void UseCustomVideoShaders(string vertexShaderFilePath, string fragmentShaderFilePath)
{
usingCustomVideoShaders = true;
customVideoVertexShaderFilepath = vertexShaderFilePath;
customVideoFragmentShaderFilepath = fragmentShaderFilePath;
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
lock (resources)
{
for (var i = resources.Count - 1; i >= 0; i--)
{
var resource = resources[i];
if (resource.TryGetTarget(out var target))
{
target.Dispose();
}
}
resources.Clear();
}
Refresh.Refresh_DestroyDevice(Handle);
}
IsDisposed = true;
}
}
~GraphicsDevice()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}