Files
Voile/Voile/Source/Rendering/StandardRenderSystem.cs

374 lines
12 KiB
C#

using System.Numerics;
using Silk.NET.Windowing;
using Silk.NET.WebGPU;
using Silk.NET.Maths;
using Voile.Utils;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
namespace Voile.Rendering
{
/// <summary>
/// A standard, WebGPU-based renderer.
/// </summary>
public class StandardRenderSystem : RenderSystem
{
/// <inheritdoc />
public override Vector2 WindowSize { get; set; }
/// <inheritdoc />
public override bool ShouldRun => !_window?.IsClosing ?? false;
public override Vector2 MonitorSize => throw new NotImplementedException();
public override int TargetFps { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override bool VSync { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override string WindowTitle { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override bool Fullscreen { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <inheritdoc />
public override void Initialize(RendererSettings settings)
{
_logger.Info("Initializing WebGPU...");
CreateApi();
CreateInstance();
CreateSurface();
CreateAdapter();
CreateDevice();
ConfigureDebugCallback();
ConfigureSurface();
}
/// <inheritdoc />
public override void CreateAndInitializeWithWindow(RendererSettings settings)
{
CreateWindow(settings.WindowSettings);
Initialize(settings);
}
public override void CreateWindow(WindowSettings windowSettings)
{
// throw new NotImplementedException();
_logger.Info("Creating window...");
WindowOptions windowOptions = WindowOptions.Default;
windowOptions.Size = new Vector2D<int>((int)windowSettings.Size.X, (int)windowSettings.Size.Y);
windowOptions.Title = windowSettings.Title;
windowOptions.API = GraphicsAPI.None;
_window = Window.Create(windowOptions);
_window.Initialize();
}
public override void BeginFrame()
{
_window!.DoEvents();
_window.DoUpdate();
_window.DoRender();
WgpuBeginFrame();
}
/// <inheritdoc />
public override void ClearBackground(Color color)
{
_clearColor = color;
}
public override void DrawText(Font font, string text, Color color)
{
throw new NotImplementedException();
}
public override void DrawSdfText(string text, int fontSize, Color color)
{
throw new NotImplementedException();
}
public override void BeginCamera2d(Vector2 offset, Vector2 target, float rotation, float zoom)
{
throw new NotImplementedException();
}
public override void EndCamera2d()
{
throw new NotImplementedException();
}
/// <inheritdoc />
public override void DrawCircle(float radius, Color color)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public override void DrawDebugText(string text, int fontSize, Color color)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public override void DrawRectangle(Vector2 size, Color color)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public override void DrawTexture(Texture2d texture, Color tint)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public override void EndFrame()
{
WgpuEndFrame();
}
/// <inheritdoc />
protected override void Shutdown()
{
_window!.DoEvents();
_window.Reset();
_window.Dispose();
ShutdownUnsafe();
}
/// <inheritdoc />
protected override double GetFrameTime()
{
return 0.0;
}
/// <inheritdoc />
protected override void SetTargetFps(int fps)
{
return;
}
/// <inheritdoc />
public override void SetTransform(Matrix4x4 transform)
{
throw new NotImplementedException();
}
/// <inheritdoc />
protected override void SetWindowTitle(string title)
{
throw new NotImplementedException();
}
/// <inheritdoc />
protected override void SetWindowVSync(bool value)
{
throw new NotImplementedException();
}
/// <inheritdoc />
protected override bool WindowShouldClose() => throw new NotImplementedException();
protected override int GetMonitorWidth(int monitorId)
{
throw new NotImplementedException();
}
protected override int GetMonitorHeight(int monitorId)
{
throw new NotImplementedException();
}
protected override int GetCurrentMonitor()
{
throw new NotImplementedException();
}
private unsafe void CreateApi()
{
_wgpu = WebGPU.GetApi();
}
private unsafe void CreateInstance()
{
InstanceDescriptor descriptor = new();
_instance = _wgpu!.CreateInstance(descriptor);
}
private unsafe void CreateSurface()
{
_surface = _window.CreateWebGPUSurface(_wgpu, _instance);
}
private unsafe void CreateAdapter()
{
RequestAdapterOptions adapterOptions = new()
{
CompatibleSurface = _surface,
// TODO: do not force Vulkan, let user choose their own API.
BackendType = BackendType.Vulkan,
PowerPreference = PowerPreference.HighPerformance
};
PfnRequestAdapterCallback callback = PfnRequestAdapterCallback.From(
(status, adapter, msgPtr, userDataPtr) =>
{
if (status == RequestAdapterStatus.Success)
{
_adapter = adapter;
AdapterProperties adapterProperties = new();
_wgpu.AdapterGetProperties(_adapter, &adapterProperties);
string deviceName = Marshal.PtrToStringAnsi((IntPtr)adapterProperties.Name);
_logger.Info($"Retrieved WebGPU Adapter ({deviceName})");
}
else
{
string msg = Marshal.PtrToStringAnsi((IntPtr)msgPtr);
_logger.Error($"Error while retrieving WebGPU Adapter: {msg}");
}
});
_wgpu.InstanceRequestAdapter(_instance, adapterOptions, callback, null);
}
private unsafe void CreateDevice()
{
DeviceDescriptor deviceDescriptor = new();
PfnRequestDeviceCallback callback = PfnRequestDeviceCallback.From(
(status, device, msgPtr, userDataPtr) =>
{
if (status == RequestDeviceStatus.Success)
{
_device = device;
_logger.Info($"Retrieved WebGPU Device.");
}
else
{
string msg = Marshal.PtrToStringAnsi((IntPtr)msgPtr);
_logger.Error($"Error while retrieving WebGPU Device: {msg}");
}
});
_wgpu.AdapterRequestDevice(_adapter, deviceDescriptor, callback, null);
}
private unsafe void ConfigureSurface()
{
SurfaceConfiguration configuration = new SurfaceConfiguration
{
Device = _device,
Width = (uint)_window!.Size.X,
Height = (uint)_window.Size.Y,
Format = Silk.NET.WebGPU.TextureFormat.Bgra8Unorm,
PresentMode = PresentMode.Immediate,
Usage = TextureUsage.RenderAttachment
};
_wgpu!.SurfaceConfigure(_surface, configuration);
}
private unsafe void ConfigureDebugCallback()
{
PfnErrorCallback callback = PfnErrorCallback.From((type, msgPtr, userDataPtr) =>
{
string msg = Marshal.PtrToStringAnsi((IntPtr)msgPtr);
_logger.Error($"WebGPU Error: {msg}");
});
_wgpu.DeviceSetUncapturedErrorCallback(_device, callback, null);
_logger.Info("WGPU Debug Callback Configured.");
}
private unsafe void WgpuBeginFrame()
{
_queue = _wgpu.DeviceGetQueue(_device);
_commandEncoder = _wgpu.DeviceCreateCommandEncoder(_device, null);
_wgpu.SurfaceGetCurrentTexture(_surface, ref _surfaceTexture);
_surfaceTextureView = _wgpu.TextureCreateView(_surfaceTexture.Texture, null);
RenderPassColorAttachment colorAttachments = CreateClearColorAttachment(_surfaceTextureView, _clearColor);
RenderPassDescriptor renderPassDescriptor = new()
{
ColorAttachments = &colorAttachments,
ColorAttachmentCount = 1
};
_renderPassEncoder = _wgpu.CommandEncoderBeginRenderPass(_commandEncoder, renderPassDescriptor);
}
private unsafe void WgpuEndFrame()
{
_wgpu.RenderPassEncoderEnd(_renderPassEncoder);
var commandBuffer = _wgpu.CommandEncoderFinish(_commandEncoder, null);
_wgpu.CommandEncoderRelease(_commandEncoder);
_wgpu.QueueSubmit(_queue, 1, &commandBuffer);
_wgpu.CommandBufferRelease(commandBuffer);
_wgpu.SurfacePresent(_surface);
_wgpu.TextureViewRelease(_surfaceTextureView);
_wgpu.TextureRelease(_surfaceTexture.Texture);
_wgpu.RenderPassEncoderRelease(_renderPassEncoder);
}
private unsafe void ShutdownUnsafe()
{
_wgpu.DeviceDestroy(_device);
_wgpu.InstanceRelease(_instance);
_wgpu.SurfaceRelease(_surface);
_wgpu.AdapterRelease(_adapter);
}
private Silk.NET.WebGPU.Color VoileColorToWebGPUColor(Color color)
{
return new Silk.NET.WebGPU.Color(color.R, color.G, color.B, color.A);
}
private unsafe RenderPassColorAttachment CreateClearColorAttachment(TextureView* view, Color clearColor)
{
var colorAttachment = new RenderPassColorAttachment
{
View = view,
LoadOp = LoadOp.Clear,
ClearValue = VoileColorToWebGPUColor(clearColor),
StoreOp = StoreOp.Store
};
return colorAttachment;
}
private unsafe RenderPipeline* CreatePipeline(Material fromMaterial)
{
return null;
}
private Vector2 _windowSize;
private IWindow? _window;
private Color _clearColor = Color.Black;
private WebGPU? _wgpu;
private unsafe Instance* _instance;
private unsafe Surface* _surface;
private unsafe Adapter* _adapter;
private unsafe Device* _device;
private unsafe Queue* _queue;
private SurfaceTexture _surfaceTexture;
private unsafe TextureView* _surfaceTextureView;
private unsafe CommandEncoder* _commandEncoder;
private unsafe RenderPassEncoder* _renderPassEncoder;
private Logger _logger = new(nameof(StandardRenderSystem));
}
}