MoonWorks/src/Graphics/GraphicsDevice.cs

272 lines
6.3 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
internal GraphicsPipeline VideoPipeline { get; }
public bool IsDisposed { get; private set; }
private readonly List<WeakReference<GraphicsResource>> resources = new List<WeakReference<GraphicsResource>>();
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)
);
// Check for optional video shaders
string basePath = SDL2.SDL.SDL_GetBasePath();
string videoVertPath = Path.Combine(basePath, "video_fullscreen.refresh");
string videoFragPath = Path.Combine(basePath, "video_yuv2rgba.refresh");
if (File.Exists(videoVertPath) && File.Exists(videoFragPath))
{
ShaderModule videoVertShader = new ShaderModule(this, videoVertPath);
ShaderModule videoFragShader = new ShaderModule(this, videoFragPath);
VideoPipeline = new GraphicsPipeline(
this,
new GraphicsPipelineCreateInfo
{
AttachmentInfo = new GraphicsPipelineAttachmentInfo(
new ColorAttachmentDescription(
TextureFormat.R8G8B8A8,
ColorAttachmentBlendState.None
)
),
DepthStencilState = DepthStencilState.Disable,
VertexShaderInfo = GraphicsShaderInfo.Create(
videoVertShader,
"main",
0
),
FragmentShaderInfo = GraphicsShaderInfo.Create(
videoFragShader,
"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);
if (window.SwapchainTexture == null)
{
window.SwapchainTexture = new Texture(this, IntPtr.Zero, window.SwapchainFormat, 0, 0);
}
}
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));
}
public unsafe void Submit(CommandBuffer commandBuffer)
{
var commandBufferPtrs = stackalloc IntPtr[1];
commandBufferPtrs[0] = commandBuffer.Handle;
Refresh.Refresh_Submit(
Handle,
1,
(IntPtr) commandBufferPtrs
);
}
public unsafe void Submit(
CommandBuffer commandBufferOne,
CommandBuffer commandBufferTwo
) {
var commandBufferPtrs = stackalloc IntPtr[2];
commandBufferPtrs[0] = commandBufferOne.Handle;
commandBufferPtrs[1] = commandBufferTwo.Handle;
Refresh.Refresh_Submit(
Handle,
2,
(IntPtr) commandBufferPtrs
);
}
public unsafe void Submit(
CommandBuffer commandBufferOne,
CommandBuffer commandBufferTwo,
CommandBuffer commandBufferThree
) {
var commandBufferPtrs = stackalloc IntPtr[3];
commandBufferPtrs[0] = commandBufferOne.Handle;
commandBufferPtrs[1] = commandBufferTwo.Handle;
commandBufferPtrs[2] = commandBufferThree.Handle;
Refresh.Refresh_Submit(
Handle,
3,
(IntPtr) commandBufferPtrs
);
}
public unsafe void Submit(
CommandBuffer commandBufferOne,
CommandBuffer commandBufferTwo,
CommandBuffer commandBufferThree,
CommandBuffer commandBufferFour
) {
var commandBufferPtrs = stackalloc IntPtr[4];
commandBufferPtrs[0] = commandBufferOne.Handle;
commandBufferPtrs[1] = commandBufferTwo.Handle;
commandBufferPtrs[2] = commandBufferThree.Handle;
commandBufferPtrs[3] = commandBufferFour.Handle;
Refresh.Refresh_Submit(
Handle,
4,
(IntPtr) commandBufferPtrs
);
}
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);
}
}
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);
}
}
}