Handle vsync and surface formats properly

This commit is contained in:
PJB3005
2025-10-09 21:28:32 +02:00
parent be7b134e1d
commit e784ac8d86
8 changed files with 80 additions and 24 deletions

View File

@@ -18,7 +18,7 @@ public abstract partial class RhiBase
/// <remarks>
/// Does not get called for the main window.
/// </remarks>
internal abstract RhiWebGpu.WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size);
internal abstract RhiWebGpu.WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size, bool vsync);
/// <summary>
/// A window is about to be destroyed by Clyde. Clean up resources for it.
@@ -28,7 +28,7 @@ public abstract partial class RhiBase
/// <summary>
/// Recreate the native swap chain, in case it has become suboptimal (e.g. due to window resizing).
/// </summary>
internal abstract void WindowRecreateSwapchain(RhiWebGpu.WindowData reg, Vector2i size);
internal abstract void WindowRecreateSwapchain(RhiWebGpu.WindowData reg, Vector2i size, bool vsyncEnabled);
internal abstract RhiTexture GetSurfaceTextureForWindow(RhiWebGpu.WindowData reg);
internal abstract void WindowPresent(RhiWebGpu.WindowData reg);
@@ -107,7 +107,6 @@ public abstract partial class RhiBase
{
public required string Backends;
public required RhiPowerPreference PowerPreference;
public required Vector2i MainWindowSize;
public required RhiWindowSurfaceParams MainWindowSurfaceParams;
}

View File

@@ -33,6 +33,8 @@ public abstract partial class RhiBase
public abstract RhiAdapterInfo AdapterInfo { get; }
public abstract string Description { get; }
public abstract RhiTextureFormat MainTextureFormat { get; }
public abstract RhiTexture CreateTexture(in RhiTextureDescriptor descriptor);
public abstract RhiSampler CreateSampler(in RhiSamplerDescriptor descriptor);

View File

@@ -76,12 +76,17 @@ internal sealed unsafe partial class RhiWebGpu
return Encoding.UTF8.GetString(span);
}
private static RhiTextureFormat ValidateTextureFormat(RhiTextureFormat format)
private static RhiTextureFormat ToRhiFormat(WGPUTextureFormat format)
{
return (RhiTextureFormat)format;
}
private static WGPUTextureFormat ValidateTextureFormat(RhiTextureFormat format)
{
if (format is 0 or >= RhiTextureFormat.Final)
throw new ArgumentException($"Invalid {nameof(RhiTextureFormat)}");
return format;
return (WGPUTextureFormat)format;
}
private static WGPUTextureDimension ValidateTextureDimension(RhiTextureDimension dimension)

View File

@@ -4,6 +4,11 @@ namespace Robust.Client.Graphics.Rhi.WebGpu;
internal sealed unsafe partial class RhiWebGpu
{
private RhiTextureFormat _mainTextureFormat;
private WGPUPresentMode[] _availPresentModes = [];
public override RhiTextureFormat MainTextureFormat => _mainTextureFormat;
public sealed class WindowData
{
public WGPUSurface Surface;
@@ -68,15 +73,46 @@ internal sealed unsafe partial class RhiWebGpu
};
}
private void ConfigureSurface(WindowData window, Vector2i size)
private void DecideMainTextureFormat(WindowData mainWindow)
{
// TODO: Safety
var format = WGPUTextureFormat.WGPUTextureFormat_BGRA8UnormSrgb;
_sawmill.Debug($"Preferred surface format is {format}");
WGPUSurfaceCapabilities surfaceCaps;
var res = wgpuSurfaceGetCapabilities(mainWindow.Surface, _wgpuAdapter, &surfaceCaps);
if (res != WGPUStatus.WGPUStatus_Success)
throw new RhiException("wgpuSurfaceGetCapabilities failed");
var modes = new Span<WGPUPresentMode>(surfaceCaps.presentModes, (int)surfaceCaps.presentModeCount);
_availPresentModes = modes.ToArray();
_sawmill.Debug($"Available present modes: {string.Join(", ", _availPresentModes)}");
var formats = new Span<WGPUTextureFormat>(surfaceCaps.formats, (int)surfaceCaps.formatCount);
var found = false;
foreach (var format in formats)
{
if (format == WGPUTextureFormat.WGPUTextureFormat_BGRA8UnormSrgb ||
format == WGPUTextureFormat.WGPUTextureFormat_RGBA8UnormSrgb)
{
found = true;
_mainTextureFormat = ToRhiFormat(format);
break;
}
}
_sawmill.Debug($"Available surface formats: {string.Join(", ", formats.ToArray())}");
if (!found)
throw new RhiException("Unable to find suitable surface format for main window!");
_sawmill.Debug($"Preferred surface format is {_mainTextureFormat}");
wgpuSurfaceCapabilitiesFreeMembers(surfaceCaps);
}
private void ConfigureSurface(WindowData window, Vector2i size, bool vsync)
{
var swapChainDesc = new WGPUSurfaceConfiguration
{
format = format,
format = ValidateTextureFormat(_mainTextureFormat),
width = (uint)size.X,
height = (uint)size.Y,
usage = WGPUTextureUsage_RenderAttachment,
@@ -84,15 +120,23 @@ internal sealed unsafe partial class RhiWebGpu
device = _wgpuDevice
};
if (!vsync)
{
if (_availPresentModes.Contains(WGPUPresentMode.WGPUPresentMode_Immediate))
swapChainDesc.presentMode = WGPUPresentMode.WGPUPresentMode_Immediate;
else if (_availPresentModes.Contains(WGPUPresentMode.WGPUPresentMode_Mailbox))
swapChainDesc.presentMode = WGPUPresentMode.WGPUPresentMode_Mailbox;
}
wgpuSurfaceConfigure(window.Surface, &swapChainDesc);
_sawmill.Debug("WebGPU Surface created!");
_sawmill.Verbose("WebGPU Surface reconfigured!");
}
internal override WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size)
internal override WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size, bool vsync)
{
var windowData = CreateSurfaceForWindow(in surfaceParams);
ConfigureSurface(windowData, size);
ConfigureSurface(windowData, size, vsync);
return windowData;
}
@@ -102,9 +146,9 @@ internal sealed unsafe partial class RhiWebGpu
wgpuSurfaceRelease(reg.Surface);
}
internal override void WindowRecreateSwapchain(WindowData reg, Vector2i size)
internal override void WindowRecreateSwapchain(WindowData reg, Vector2i size, bool vsyncEnabled)
{
ConfigureSurface(reg, size);
ConfigureSurface(reg, size, vsyncEnabled);
}
internal override void WindowPresent(WindowData reg)

View File

@@ -52,8 +52,7 @@ internal sealed unsafe partial class RhiWebGpu : RhiBase
_sawmill.Debug("WebGPU main surface created!");
InitAdapterAndDevice(in initParams, windowData.Surface);
ConfigureSurface(windowData, initParams.MainWindowSize);
DecideMainTextureFormat(windowData);
}
private void InitInstance(in RhiInitParams initParams)

View File

@@ -30,7 +30,6 @@ internal sealed partial class Clyde
{
Backends = _cfg.GetCVar(CVars.DisplayWgpuBackends),
PowerPreference = (RhiPowerPreference)_cfg.GetCVar(CVars.DisplayGpuPowerPreference),
MainWindowSize = _mainWindow!.FramebufferSize,
MainWindowSurfaceParams = _mainWindow.SurfaceParams
},
out _mainWindow.RhiWebGpuData);
@@ -50,7 +49,7 @@ internal sealed partial class Clyde
if (window.NeedSurfaceReconfigure)
{
Rhi.WindowRecreateSwapchain(window.RhiWebGpuData!, window.FramebufferSize);
Rhi.WindowRecreateSwapchain(window.RhiWebGpuData!, window.FramebufferSize, VsyncEnabled);
window.NeedSurfaceReconfigure = false;
}
@@ -58,7 +57,7 @@ internal sealed partial class Clyde
window.CurSurfaceTextureView = window.CurSurfaceTexture.CreateView(new RhiTextureViewDescriptor
{
Dimension = RhiTextureViewDimension.Dim2D,
Format = RhiTextureFormat.BGRA8UnormSrgb,
Format = Rhi.MainTextureFormat,
ArrayLayerCount = 1,
MipLevelCount = 1,
Aspect = RhiTextureAspect.All,

View File

@@ -166,7 +166,7 @@ internal partial class Clyde
new[]
{
new RhiColorTargetState(
RhiTextureFormat.BGRA8UnormSrgb,
rhi.MainTextureFormat,
new RhiBlendState(
new RhiBlendComponent(
RhiBlendOperation.Add,

View File

@@ -289,7 +289,7 @@ namespace Robust.Client.Graphics.Clyde
reg.RenderTarget = renderTarget;
if (!isMain)
reg.RhiWebGpuData = Rhi.WindowCreated(in reg.SurfaceParams, reg.FramebufferSize);
reg.RhiWebGpuData = Rhi.WindowCreated(in reg.SurfaceParams, reg.FramebufferSize, VsyncEnabled);
}
// Pass through result whether successful or not, caller handles it.
@@ -329,7 +329,15 @@ namespace Robust.Client.Graphics.Clyde
public bool VsyncEnabled
{
get => _vSync;
set => _vSync = value;
set
{
_vSync = value;
foreach (var window in _windows)
{
window.NeedSurfaceReconfigure = true;
}
}
}
private void WindowModeChanged(int mode)
@@ -422,7 +430,7 @@ namespace Robust.Client.Graphics.Clyde
public bool DisposeOnClose;
public bool NeedSurfaceReconfigure;
public bool NeedSurfaceReconfigure = true;
public RhiBase.RhiWindowSurfaceParams SurfaceParams;
public bool IsMainWindow;