-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
68 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 67 additions & 43 deletions
110
addons/imgui-godot/ImGuiGodot/Internal/RdRendererThreadSafe.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,126 @@ | ||
using Godot; | ||
using ImGuiNET; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Runtime.InteropServices; | ||
|
||
using SharedList = ImGuiGodot.Internal.DisposableList<Godot.Rid, ImGuiGodot.Internal.ClonedDrawData>; | ||
|
||
namespace ImGuiGodot.Internal; | ||
|
||
internal sealed class RdRendererThreadSafe : RdRenderer, IRenderer | ||
internal sealed class ClonedDrawData : IDisposable | ||
{ | ||
public new string Name => "imgui_impl_godot4_rd_mt"; | ||
public ImDrawDataPtr Data { get; private set; } | ||
|
||
private readonly object _sharedDataLock = new(); | ||
private Tuple<Rid, ImDrawDataPtr>[] _dataToDraw = null; | ||
|
||
public RdRendererThreadSafe() : base() | ||
public unsafe ClonedDrawData(ImDrawDataPtr inp) | ||
{ | ||
// draw on the renderer thread to avoid conflicts | ||
RenderingServer.FramePreDraw += OnFramePreDraw; | ||
} | ||
long ddsize = Marshal.SizeOf<ImDrawData>(); | ||
|
||
~RdRendererThreadSafe() | ||
{ | ||
RenderingServer.FramePreDraw -= OnFramePreDraw; | ||
// start with a shallow copy | ||
Data = new(ImGui.MemAlloc((uint)ddsize)); | ||
Buffer.MemoryCopy(inp.NativePtr, Data.NativePtr, ddsize, ddsize); | ||
|
||
// clone the draw data | ||
Data.NativePtr->CmdLists = (ImDrawList**)ImGui.MemAlloc((uint)(Marshal.SizeOf<IntPtr>() * inp.CmdListsCount)); | ||
for (int i = 0; i < inp.CmdListsCount; ++i) | ||
{ | ||
Data.NativePtr->CmdLists[i] = inp.CmdListsRange[i].CloneOutput().NativePtr; | ||
} | ||
} | ||
|
||
private static unsafe ImDrawDataPtr CopyDrawData(ImDrawDataPtr drawData) | ||
public unsafe void Dispose() | ||
{ | ||
long ddsize = Marshal.SizeOf<ImDrawData>(); | ||
ImDrawDataPtr rv = new(ImGui.MemAlloc((uint)ddsize)); | ||
Buffer.MemoryCopy(drawData.NativePtr, rv.NativePtr, ddsize, ddsize); | ||
rv.CmdLists = ImGui.MemAlloc((uint)(Marshal.SizeOf<IntPtr>() * drawData.CmdListsCount)); | ||
if (Data.NativePtr == null) | ||
return; | ||
|
||
for (int i = 0; i < drawData.CmdListsCount; ++i) | ||
for (int i = 0; i < Data.CmdListsCount; ++i) | ||
{ | ||
rv.NativePtr->CmdLists[i] = drawData.CmdListsRange[i].CloneOutput().NativePtr; | ||
Data.CmdListsRange[i].Destroy(); | ||
} | ||
return rv; | ||
ImGui.MemFree(Data.CmdLists); | ||
Data.Destroy(); | ||
Data = new(null); | ||
} | ||
} | ||
|
||
internal sealed class DisposableList<T, U> : List<Tuple<T, U>>, IDisposable where U : IDisposable | ||
{ | ||
public DisposableList() : base() { } | ||
public DisposableList(int capacity) : base(capacity) { } | ||
|
||
private static unsafe void FreeDrawData(ImDrawDataPtr drawData) | ||
public void Dispose() | ||
{ | ||
for (int i = 0; i < drawData.CmdListsCount; ++i) | ||
foreach (var tuple in this) | ||
{ | ||
drawData.CmdListsRange[i].Destroy(); | ||
tuple.Item2.Dispose(); | ||
} | ||
ImGui.MemFree(drawData.CmdLists); | ||
ImGui.MemFree((IntPtr)drawData.NativePtr); | ||
Clear(); | ||
} | ||
} | ||
|
||
private static void FreeAll(Tuple<Rid, ImDrawDataPtr>[] array) | ||
internal sealed class RdRendererThreadSafe : RdRenderer, IRenderer | ||
{ | ||
public new string Name => "imgui_impl_godot4_rd_mt"; | ||
|
||
private readonly object _sharedDataLock = new(); | ||
private SharedList _dataToDraw; | ||
|
||
public RdRendererThreadSafe() : base() | ||
{ | ||
foreach (var kv in array) | ||
{ | ||
FreeDrawData(kv.Item2); | ||
} | ||
// draw on the renderer thread to avoid conflicts | ||
RenderingServer.FramePreDraw += OnFramePreDraw; | ||
} | ||
|
||
~RdRendererThreadSafe() | ||
{ | ||
RenderingServer.FramePreDraw -= OnFramePreDraw; | ||
} | ||
|
||
public new void RenderDrawData() | ||
{ | ||
var pio = ImGui.GetPlatformIO(); | ||
var newData = new Tuple<Rid, ImDrawDataPtr>[pio.Viewports.Size]; | ||
var newData = new SharedList(pio.Viewports.Size); | ||
|
||
for (int i = 0; i < pio.Viewports.Size; ++i) | ||
{ | ||
// TODO: skip minimized windows | ||
var vp = pio.Viewports[i]; | ||
if (vp.Flags.HasFlag(ImGuiViewportFlags.IsMinimized)) | ||
continue; | ||
|
||
ReplaceTextureRids(vp.DrawData); | ||
Rid vprid = Util.ConstructRid((ulong)vp.RendererUserData); | ||
newData[i] = new(GetFramebuffer(vprid), CopyDrawData(vp.DrawData)); | ||
newData.Add(new(GetFramebuffer(vprid), new(vp.DrawData))); | ||
} | ||
|
||
lock (_sharedDataLock) | ||
{ | ||
// if a frame was skipped, free old data | ||
if (_dataToDraw != null) | ||
FreeAll(_dataToDraw); | ||
_dataToDraw?.Dispose(); | ||
_dataToDraw = newData; | ||
} | ||
} | ||
|
||
private void OnFramePreDraw() | ||
private SharedList TakeSharedData() | ||
{ | ||
Tuple<Rid, ImDrawDataPtr>[] dataArray = null; | ||
lock (_sharedDataLock) | ||
{ | ||
// take ownership of shared data | ||
dataArray = _dataToDraw; | ||
var rv = _dataToDraw; | ||
_dataToDraw = null; | ||
return rv ?? new(); | ||
} | ||
} | ||
|
||
if (dataArray == null) | ||
return; | ||
private void OnFramePreDraw() | ||
{ | ||
// take ownership of shared data | ||
using SharedList dataArray = TakeSharedData(); | ||
|
||
foreach (var kv in dataArray) | ||
{ | ||
if (RD.FramebufferIsValid(kv.Item1)) | ||
RenderOne(kv.Item1, kv.Item2); | ||
RenderOne(kv.Item1, kv.Item2.Data); | ||
} | ||
FreeAll(dataArray); | ||
|
||
FreeUnusedTextures(); | ||
} | ||
} |