Skip to content

Commit

Permalink
Implement GPU acceleration for VR overlays
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaminZehowlt committed Oct 23, 2024
1 parent ee34b2c commit 67000fd
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 102 deletions.
60 changes: 60 additions & 0 deletions src-overlay-sidecar/Managers/OvrManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
using SharpDX.DXGI;
using Valve.VR;
using Device = SharpDX.Direct3D11.Device;
using Device1 = SharpDX.Direct3D11.Device1;
using Device2 = SharpDX.Direct3D11.Device2;
using Device3 = SharpDX.Direct3D11.Device3;
using Device4 = SharpDX.Direct3D11.Device4;

namespace overlay_sidecar;

Expand All @@ -24,6 +28,9 @@ public class OvrManager {
private MicMuteIndicatorOverlay? _micMuteIndicatorOverlay;
private bool _active;
private Device? _device;
private Device1? _device1;
private DeviceMultithread? _deviceMultithread;
private Query? _query;
private Dictionary<string, List<OvrInputDevice>> inputActions = new();
public event EventHandler<Dictionary<string, List<OvrInputDevice>>> OnInputActionsChanged;

Expand All @@ -35,6 +42,8 @@ public class OvrManager {
public OverlayPointer? OverlayPointer => _overlayPointer;

public Device D3D11Device => _device!;
public Device1? D3D11Device1 => _device1;
public Query D3D11Query => _query!;

private OvrManager()
{
Expand Down Expand Up @@ -65,6 +74,18 @@ private async Task InitializeDevice()
DeviceCreationFlags.BgraSupport)
: new Device(DriverType.Hardware,
DeviceCreationFlags.BgraSupport);
UpgradeDevice();

_device1 = _device.QueryInterface<Device1>();

_deviceMultithread = _device.QueryInterfaceOrNull<DeviceMultithread>();
_deviceMultithread?.SetMultithreadProtected(true);

_query = new Query(_device, new QueryDescription
{
Type = QueryType.Event,
Flags = QueryFlags.None
});
}
catch (SharpDXException err)
{
Expand All @@ -82,6 +103,45 @@ private async Task InitializeDevice()
}
}

// Upgrades the device to the newest supported interface.
private void UpgradeDevice()
{
Device5 device5 = _device!.QueryInterfaceOrNull<Device5>();
if (device5 != null)
{
_device.Dispose();
_device = device5;
return;
}
Device4 device4 = _device.QueryInterfaceOrNull<Device4>();
if (device4 != null)
{
_device.Dispose();
_device = device4;
return;
}
Device3 device3 = _device.QueryInterfaceOrNull<Device3>();
if (device3 != null)
{
_device.Dispose();
_device = device3;
return;
}
Device2 device2 = _device.QueryInterfaceOrNull<Device2>();
if (device2 != null)
{
_device.Dispose();
_device = device2;
return;
}
Device1 device1 = _device.QueryInterfaceOrNull<Device1>();
if (device1 != null)
{
_device.Dispose();
_device = device1;
}
}

private void OverlayRenderLoop()
{
var timer = new RefreshRateTimer();
Expand Down
5 changes: 3 additions & 2 deletions src-overlay-sidecar/Overlays/BaseWebOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ private async void Init(int resolution)
CpuAccessFlags = CpuAccessFlags.Write
}
);

Browser!.UpdateTexture(_texture);
}
catch (SharpDXException err)
{
Expand Down Expand Up @@ -210,8 +212,7 @@ public void UpdateFrame()
// Stop here if we are not ready, already disposed, or if the browser hasn't painted anything new for the past second or so.
if (_texture == null || Disposed || Browser == null ||
DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - Browser.LastPaint >= 1000) return;
// Render the browser to the texture
Browser.RenderToTexture(_texture);

var texture = new Texture_t
{
handle = _texture.NativePointer
Expand Down
149 changes: 49 additions & 100 deletions src-overlay-sidecar/Overlays/Helpers/OffScreenBrowser.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Source: https://github.com/vrcx-team/VRCX/blob/master/OffScreenBrowser.cs

using SharpDX.Mathematics.Interop;

namespace overlay_sidecar;

using CefSharp;
Expand All @@ -12,25 +14,32 @@ namespace overlay_sidecar;
using System.Threading;

public class OffScreenBrowser : ChromiumWebBrowser, IRenderHandler {
private readonly ReaderWriterLockSlim _paintBufferLock;
private GCHandle _paintBuffer;
private int _width;
private int _height;
private Texture2D? _texture;
private long _lastPaint;
public long LastPaint => _lastPaint;

public OffScreenBrowser(string address, int width, int height)
: base(
address,
new BrowserSettings()
{
WindowlessFrameRate = 60,
WebGl = CefState.Enabled,
DefaultEncoding = "UTF-8"
}
automaticallyCreateBrowser: false
)
{
_paintBufferLock = new ReaderWriterLockSlim();
var windowInfo = new WindowInfo();
windowInfo.SetAsWindowless(IntPtr.Zero);
windowInfo.WindowlessRenderingEnabled = true;
windowInfo.SharedTextureEnabled = true;
windowInfo.Width = width;
windowInfo.Height = height;

var browserSettings = new BrowserSettings()
{
WindowlessFrameRate = 60,
WebGl = CefState.Enabled,
DefaultEncoding = "UTF-8"
};

CreateBrowser(windowInfo, browserSettings);

Size = new System.Drawing.Size(width, height);
RenderHandler = this;
}
Expand All @@ -40,72 +49,19 @@ public OffScreenBrowser(string address, int width, int height)
RenderHandler = null;
if (IsDisposed) return;
base.Dispose();

_paintBufferLock.EnterWriteLock();
try
{
if (_paintBuffer.IsAllocated) _paintBuffer.Free();
}
finally
{
_paintBufferLock.ExitWriteLock();
}

_paintBufferLock.Dispose();
}

public void RenderToTexture(Texture2D texture)
public void UpdateTexture(Texture2D texture)
{
_paintBufferLock.EnterReadLock();
try
{
if (_width > 0 &&
_height > 0)
{
var context = texture.Device.ImmediateContext;
var dataBox = context.MapSubresource(
texture,
0,
MapMode.WriteDiscard,
MapFlags.None
);
if (dataBox.IsEmpty == false)
{
var sourcePtr = _paintBuffer.AddrOfPinnedObject();
var destinationPtr = dataBox.DataPointer;
var pitch = _width * 4;
var rowPitch = dataBox.RowPitch;
if (pitch == rowPitch)
WinApi.CopyMemory(
destinationPtr,
sourcePtr,
(uint)(_width * _height * 4)
);
else
for (var y = _height; y > 0; --y)
{
WinApi.CopyMemory(
destinationPtr,
sourcePtr,
(uint)pitch
);
sourcePtr += pitch;
destinationPtr += rowPitch;
}
}
context.UnmapSubresource(texture, 0);
}
}
finally

{
_paintBufferLock.ExitReadLock();
}
_texture = texture;
}

ScreenInfo? IRenderHandler.GetScreenInfo()
{
return null;
return new ScreenInfo
{
DeviceScaleFactor = 1.0F
};
}

bool IRenderHandler.GetScreenPoint(int viewX, int viewY, out int screenX, out int screenY)
Expand All @@ -120,8 +76,30 @@ Rect IRenderHandler.GetViewRect()
return new Rect(0, 0, Size.Width, Size.Height);
}

void IRenderHandler.OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, IntPtr sharedHandle)
void IRenderHandler.OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, AcceleratedPaintInfo paintInfo)
{
if (type != PaintElementType.View) return;
if (OvrManager.Instance.D3D11Device1 == null) return;
if (_texture == null) return;

using var cefTexture =
OvrManager.Instance.D3D11Device1.OpenSharedResource1<Texture2D>(paintInfo.SharedTextureHandle);
var context = OvrManager.Instance.D3D11Device.ImmediateContext;
context.CopyResource(cefTexture, _texture);

Query query = OvrManager.Instance.D3D11Query;
context.End(query);
context.Flush();

RawBool q = context.GetData<RawBool>(query, AsynchronousFlags.DoNotFlush);

while (!q)
{
Thread.Yield();
q = context.GetData<RawBool>(query, AsynchronousFlags.DoNotFlush);
}

_lastPaint = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}

void IRenderHandler.OnCursorChange(IntPtr cursor, CursorType type, CursorInfo customCursorInfo)
Expand All @@ -134,35 +112,6 @@ void IRenderHandler.OnImeCompositionRangeChanged(CefSharp.Structs.Range selected

void IRenderHandler.OnPaint(PaintElementType type, Rect dirtyRect, IntPtr buffer, int width, int height)
{
if (type != PaintElementType.View) return;
_paintBufferLock.EnterWriteLock();
try
{
if (_width != width ||
_height != height)
{
_width = width;
_height = height;
if (_paintBuffer.IsAllocated) _paintBuffer.Free();

_paintBuffer = GCHandle.Alloc(
new byte[_width * _height * 4],
GCHandleType.Pinned
);
}

WinApi.CopyMemory(
_paintBuffer.AddrOfPinnedObject(),
buffer,
(uint)(width * height * 4)
);

_lastPaint = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
finally
{
_paintBufferLock.ExitWriteLock();
}
}

void IRenderHandler.OnPopupShow(bool show)
Expand Down

0 comments on commit 67000fd

Please sign in to comment.