Actual working viewports :)

This commit is contained in:
2026-05-18 10:43:19 -05:00
parent fc0b04a4bc
commit 7167d98cb6
5 changed files with 234 additions and 268 deletions
+2 -2
View File
@@ -29,8 +29,8 @@ public static class FontFind
FileInfo info = new FileInfo(fontPath);
if (!info.Exists || info.Length <= 0) return null;
FontMetrics metrics = FontFind.ReadFontMetrics(fontPath);
float size = FontFind.GetRecommendedPixelSize(metrics);
FontMetrics metrics = ReadFontMetrics(fontPath);
float size = GetRecommendedPixelSize(metrics);
ImFontConfigPtr config = ImGui.ImFontConfig();
config.FontLoaderFlags |= (uint)ImGuiFreeTypeLoaderFlags.LoadColor;
+113 -79
View File
@@ -67,28 +67,35 @@ public unsafe static class ImGuiSDL3Platform
public bool RendererOwned;
}
public static SDL3Window Parent;
// ReSharper disable NotAccessedField.Local
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate nint PlatformGetClipboardTextFn(nint ctx);
private static readonly PlatformGetClipboardTextFn GetClipboardDelegate = GetClipboardText;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void PlatformSetClipboardTextFn(nint ctx, nint text);
private static readonly PlatformSetClipboardTextFn SetClipboardDelegate = SetClipboardText;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void PlatformSetImeDataFn(nint ctx, nint userData, nint imeData);
private static readonly PlatformSetImeDataFn SetImeDataDelegate = SetImeData;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool PlatformOpenInShellFn(nint ctx, nint url);
private static readonly PlatformOpenInShellFn OpenInShellDelegate = OpenInShell;
// ReSharper restore NotAccessedField.Local
public static PlatformData Data => ImGui.GetCurrentContext().Handle != null ? ImGuiUserData<PlatformData>.Get(ImGui.GetIO().BackendPlatformUserData)! : null!;
public static bool Init(nint window, nint renderer)
public static bool Init(nint window, nint renderer, SDL3Window parentWindow = null)
{
Parent = parentWindow;
ImGuiIOPtr io = ImGui.GetIO();
PlatformData data = new PlatformData
{
@@ -184,7 +191,7 @@ public unsafe static class ImGuiSDL3Platform
{
UpdateMonitors();
}
ulong frequency = SDL.GetPerformanceFrequency();
ulong currentTime = SDL.GetPerformanceCounter();
if (currentTime <= data.Time)
@@ -211,11 +218,11 @@ public unsafe static class ImGuiSDL3Platform
io.BackendFlags &= ~ImGuiBackendFlags.HasMouseHoveredViewport;
}
UpdateMouseData(data);
UpdateMouseCursor(data);
UpdateIme(data);
UpdateMouseData();
UpdateMouseCursor();
UpdateIme();
UpdateGamepads(data);
UpdateGamepads();
}
public static bool ProcessEvent(SDL.Event e)
@@ -230,7 +237,9 @@ public unsafe static class ImGuiSDL3Platform
return false;
io.AddMouseSourceEvent(e.Motion.Which == SDL.TouchMouseID ? ImGuiMouseSource.TouchScreen : ImGuiMouseSource.Mouse);
io.AddMousePosEvent(e.Motion.X, e.Motion.Y);
// ImGui expects desktop/global coordinates when viewports are enabled.
SDL.GetGlobalMouseState(out float mouseX, out float mouseY);
io.AddMousePosEvent(mouseX, mouseY);
return true;
case SDL.EventType.MouseWheel:
if (GetViewportForWindowId(e.Wheel.WindowID) == null)
@@ -306,36 +315,105 @@ public unsafe static class ImGuiSDL3Platform
return false;
}
private static void UpdateMouseData(PlatformData data)
{
if (SDL.GetKeyboardFocus() == data.Window && ImGui.GetIO().WantSetMousePos)
SDL.WarpMouseInWindow(data.Window, (int)ImGui.GetIO().MousePos.X, (int)ImGui.GetIO().MousePos.Y);
}
private static void UpdateMouseCursor(PlatformData data)
// This code is rather messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4.
private static void UpdateMouseData()
{
var bd = Data;
ImGuiIOPtr io = ImGui.GetIO();
// We forward mouse input when hovered or captured (via SDL.MOUSEMOTION) or when focused (below)
// - SDL.CaptureMouse() let the OS know e.g. that our drags can extend outside of parent boundaries (we want updated position) and shouldn't trigger other operations outside.
// - Debuggers under Linux tends to leave captured mouse on break, which may be very inconvenient, so to mitigate the issue on X11 we we wait until mouse has moved to begin capture.
if (bd.MouseCaptureMode == MouseCaptureMode.Enabled)
{
SDL.CaptureMouse((bd.MouseButtonsDown != 0) ? true : false);
}
else if (bd.MouseCaptureMode == MouseCaptureMode.EnabledAfterDrag)
{
bool wantCapture = false;
for (int buttonN = 0; buttonN < (int)ImGuiMouseButton.Count && !wantCapture; buttonN++)
{
if (ImGui.IsMouseDragging((ImGuiMouseButton)buttonN, 1.0f))
wantCapture = true;
}
SDL.CaptureMouse(wantCapture ? true : false);
}
nint focusedWindow = SDL.GetKeyboardFocus();
bool isAppFocused = (focusedWindow != nint.Zero && (bd.Window == focusedWindow || GetViewportForWindowId(SDL.GetWindowID(focusedWindow)) != null));
if (isAppFocused)
{
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
if (io.WantSetMousePos)
{
if ((io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable)))
SDL.WarpMouseGlobal((int)io.MousePos.X, (int)io.MousePos.Y);
else
SDL.WarpMouseInWindow(bd.Window, (int)io.MousePos.X, (int)io.MousePos.Y);
}
// (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL.MOUSEMOTION already provides this when hovered or captured)
// Note that SDL.GetGlobalMouseState() is in theory slow on X11, but this only runs on rather specific cases. If a problem we may provide a way to opt-out this feature.
nint hoveredWindow = SDL.GetMouseFocus();
bool isRelativeMouseMode = false /*SDL.GetRelativeMouseMode() != 0*/;
if (hoveredWindow == nint.Zero && bd.MouseCanUseGlobalState && bd.MouseButtonsDown == 0 && !isRelativeMouseMode)
{
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
// Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
SDL.GetGlobalMouseState(out float mouseX, out float mouseY);
if (!io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable))
{
SDL.GetWindowPosition(focusedWindow, out int windowX, out int windowY);
mouseX -= windowX;
mouseY -= windowY;
}
io.AddMousePosEvent(mouseX, mouseY);
}
}
// (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
// - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag.
// Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
// for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
// by the backend, and use its flawed heuristic to guess the viewport behind.
// - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
if ((io.BackendFlags.HasFlag(ImGuiBackendFlags.HasMouseHoveredViewport)))
{
uint mouseViewportID = 0;
var mouseViewport = GetViewportForWindowId(bd.MouseWindowID);
if (mouseViewport != null)
mouseViewportID = mouseViewport.Value.ID;
io.AddMouseViewportEvent(mouseViewportID);
}
}
private static void UpdateMouseCursor()
{
ImGuiIOPtr io = ImGui.GetIO();
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0)
return;
ImGuiMouseCursor cursor = ImGui.GetMouseCursor();
var bd = Data;
if (io.MouseDrawCursor || cursor == ImGuiMouseCursor.None)
ImGuiMouseCursor imguiCursor = ImGui.GetMouseCursor();
if (io.MouseDrawCursor || imguiCursor == ImGuiMouseCursor.None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL.HideCursor();
return;
}
nint expected = data.MouseCursors[(int)cursor];
if (data.MouseLastCursor != expected)
else
{
SDL.SetCursor(expected);
data.MouseLastCursor = expected;
// Show OS mouse cursor
nint expectedCursor = bd.MouseCursors[(int)imguiCursor] != nint.Zero ? bd.MouseCursors[(int)imguiCursor] : bd.MouseCursors[(int)ImGuiMouseCursor.Arrow];
if (bd.MouseLastCursor != expectedCursor)
{
SDL.SetCursor(expectedCursor); // SDL function doesn't have an early out (see #6113)
bd.MouseLastCursor = expectedCursor;
}
SDL.ShowCursor();
}
SDL.ShowCursor();
}
private static void CloseGamepads()
@@ -416,8 +494,9 @@ public unsafe static class ImGuiSDL3Platform
io.AddKeyAnalogEvent(key, mergedValue > 0.1f, mergedValue);
}
private static void UpdateGamepads(PlatformData data)
private static void UpdateGamepads()
{
PlatformData data = Data;
ImGuiIOPtr io = ImGui.GetIO();
if (data.WantUpdateGamepadsList && data.GamepadMode != GamepadMode.Manual)
@@ -664,8 +743,9 @@ public unsafe static class ImGuiSDL3Platform
data.ImeDirty = true;
}
public static void UpdateIme(PlatformData data)
public static void UpdateIme()
{
PlatformData data = Data;
ImGuiPlatformImeData imeData = data.ImeData;
nint window = SDL.GetKeyboardFocus();
@@ -723,7 +803,6 @@ public unsafe static class ImGuiSDL3Platform
private static void CreateWindow(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
PlatformData bd = Data;
ViewPortData vd = new ViewPortData();
@@ -733,10 +812,7 @@ public unsafe static class ImGuiSDL3Platform
flags |= SDL.GetWindowFlags(bd.Window) & SDL.WindowFlags.HighPixelDensity;
string? videoDriver = SDL.GetCurrentVideoDriver();
bool isWayland = string.Equals(videoDriver, "wayland", StringComparison.OrdinalIgnoreCase);
if ((viewport.Flags & ImGuiViewportFlags.NoDecoration) != 0 && !isWayland)
if ((viewport.Flags & ImGuiViewportFlags.NoDecoration) != 0)
{
flags |= SDL.WindowFlags.Borderless;
}
@@ -755,7 +831,7 @@ public unsafe static class ImGuiSDL3Platform
flags |= SDL.WindowFlags.AlwaysOnTop;
}
vd.Window = SDL.CreateWindow("Untitled", (int)viewport.Size.X, (int)viewport.Size.Y, flags);
vd.Window = SDL.CreateWindow("Untitled", (int)viewport.Size.X, (int)viewport.Size.Y, flags | SDL.WindowFlags.Transparent);
ImGuiViewportPtr? parentViewport = viewport.ParentViewportId != 0 ? ImGui.FindViewportByID(viewport.ParentViewportId) : null;
if (parentViewport != null && parentViewport.Value.Handle != null)
@@ -783,7 +859,6 @@ public unsafe static class ImGuiSDL3Platform
private static void DestroyWindow(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
ViewPortData? vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData);
if (vd != null)
@@ -799,13 +874,10 @@ public unsafe static class ImGuiSDL3Platform
viewport.PlatformUserData = null;
viewport.PlatformHandle = null;
viewport.PlatformHandleRaw = null;
Program.Logger.Log(null);
}
private static void ShowWindow(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
string? oldHint = SDL.GetHint(SDL.Hints.WindowActivateWhenShown);
@@ -815,12 +887,10 @@ public unsafe static class ImGuiSDL3Platform
SDL.ShowWindow(vd.Window);
SDL.SetHint(SDL.Hints.WindowActivateWhenShown, oldHint);
Program.Logger.Log(null);
}
private static void UpdateWindow(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
if ((viewport.Flags & ImGuiViewportFlags.TopMost) != 0)
@@ -831,7 +901,6 @@ public unsafe static class ImGuiSDL3Platform
{
SDL.SetWindowAlwaysOnTop(vd.Window, false);
}
Program.Logger.Log(null);
}
private static Vector2 GetWindowPos(ImGuiViewportPtr viewport)
@@ -839,8 +908,6 @@ public unsafe static class ImGuiSDL3Platform
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
SDL.GetWindowPosition(vd.Window, out int x, out int y);
// Program.Logger.Log(null);
return new Vector2(x, y);
}
@@ -850,7 +917,6 @@ public unsafe static class ImGuiSDL3Platform
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
SDL.SetWindowPosition(vd.Window, (int)pos.X, (int)pos.Y);
Program.Logger.Log(null);
}
private static Vector2 GetWindowSize(ImGuiViewportPtr viewport)
@@ -859,7 +925,6 @@ public unsafe static class ImGuiSDL3Platform
SDL.GetWindowSize(vd.Window, out int w, out int h);
// Program.Logger.Log(null);
return new Vector2(w, h);
}
@@ -868,7 +933,6 @@ public unsafe static class ImGuiSDL3Platform
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
SDL.SetWindowSize(vd.Window, (int)size.X, (int)size.Y);
Program.Logger.Log(null);
}
private static Vector2 GetWindowFramebufferScale(ImGuiViewportPtr viewport)
@@ -876,7 +940,6 @@ public unsafe static class ImGuiSDL3Platform
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
GetWindowSizeAndFramebufferScale(vd.Window, out Vector2 framebufferSize, out Vector2 framebufferScale);
// Program.Logger.Log(null);
return framebufferScale;
}
@@ -886,7 +949,6 @@ public unsafe static class ImGuiSDL3Platform
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
SDL.SetWindowTitle(vd.Window, title);
Program.Logger.Log(null);
}
private static void SetWindowAlpha(ImGuiViewportPtr viewport, float alpha)
@@ -894,7 +956,6 @@ public unsafe static class ImGuiSDL3Platform
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
SDL.SetWindowOpacity(vd.Window, alpha);
Program.Logger.Log(null);
}
private static void SetWindowFocus(ImGuiViewportPtr viewport)
@@ -902,13 +963,11 @@ public unsafe static class ImGuiSDL3Platform
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
SDL.RaiseWindow(vd.Window);
Program.Logger.Log(null);
}
private static bool GetWindowFocus(ImGuiViewportPtr viewport)
{
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
// Program.Logger.Log(null);
return (SDL.GetWindowFlags(vd.Window) & SDL.WindowFlags.InputFocus) != 0;
}
@@ -916,23 +975,10 @@ public unsafe static class ImGuiSDL3Platform
private static bool GetWindowMinimized(ImGuiViewportPtr viewport)
{
ViewPortData vd = ImGuiUserData<ViewPortData>.Get(viewport.PlatformUserData)!;
// Program.Logger.Log(null);
return (SDL.GetWindowFlags(vd.Window) & SDL.WindowFlags.Minimized) != 0;
}
private static void RenderWindow(ImGuiViewportPtr viewport)
{
// Renderer-backed viewport rendering is handled by ImGuiSDL3Renderer.
Program.Logger.Log(null);
}
private static void SwapBuffers(ImGuiViewportPtr viewport)
{
// Renderer-backed viewport presentation is handled by ImGuiSDL3Renderer.
Program.Logger.Log(null);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void CreateWindowDelegatelFn(ImGuiViewportPtr ctx);
@@ -999,16 +1045,6 @@ public unsafe static class ImGuiSDL3Platform
private static readonly SetWindowTitleDelegatelFn SetWindowTitleDelegate = SetWindowTitle;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void RenderWindowDelegatelFn(ImGuiViewportPtr ctx);
private static readonly RenderWindowDelegatelFn RenderWindowDelegate = RenderWindow;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void SwapBuffersDelegatelFn(ImGuiViewportPtr ctx);
private static readonly SwapBuffersDelegatelFn SwapBuffersDelegate = SwapBuffers;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void SetWindowAlphaDelegatelFn(ImGuiViewportPtr ctx, float alpha);
@@ -1016,8 +1052,7 @@ public unsafe static class ImGuiSDL3Platform
private static void InitMultiViewportSupport(nint window)
{
Program.Logger.Log(null);
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
platformIO.PlatformCreateWindow = (void*)Marshal.GetFunctionPointerForDelegate(CreateWindowDelegate);
@@ -1048,8 +1083,7 @@ public unsafe static class ImGuiSDL3Platform
mainViewport.PlatformUserData = ImGuiUserData<ViewPortData>.Store(vd);
mainViewport.PlatformHandle = (void*)(nint)SDL.GetWindowID(window);
Program.Logger.Log(null);
#if WINDOWS
mainViewport.PlatformHandleRaw = SDL.GetPointerProperty(
@@ -1080,7 +1114,7 @@ public unsafe static class ImGuiSDL3Platform
SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowCocoaWindow, 0);
#endif
}
public static void Dispose()
{
PlatformData? data = Data;
@@ -1149,4 +1183,4 @@ public unsafe static class ImGuiUserData<T> where T : class
if (handle.IsAllocated)
handle.Free();
}
}
}
+77 -155
View File
@@ -11,11 +11,7 @@ namespace SDL3_TestingSuite.SDL3;
/// </summary>
public unsafe static class ImGuiSDL3Renderer
{
private sealed class TextureState
{
public ImTextureDataPtr Source;
public readonly Dictionary<nint, nint> RendererTextures = new();
}
public static SDL3Window Parent;
public class RendererData
{
@@ -44,11 +40,6 @@ public unsafe static class ImGuiSDL3Renderer
private static readonly RendererDestroyWindowFn RendererDestroyWindowDelegate = RendererDestroyWindow;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void RendererSetWindowSizeFn(ImGuiViewportPtr viewport, Vector2 size);
private static readonly RendererSetWindowSizeFn RendererSetWindowSizeDelegate = RendererSetWindowSize;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void RendererRenderWindowFn(ImGuiViewportPtr viewport);
@@ -61,8 +52,9 @@ public unsafe static class ImGuiSDL3Renderer
public static RendererData Data => ImGui.GetCurrentContext().Handle != null ? ImGuiUserData<RendererData>.Get(ImGui.GetIO().BackendRendererUserData)! : null!;
public static bool Init(nint renderer)
public static bool Init(nint renderer, SDL3Window parentWindow = null)
{
Parent = parentWindow;
ImGuiIOPtr io = ImGui.GetIO();
RendererData bd = new RendererData();
@@ -77,7 +69,6 @@ public unsafe static class ImGuiSDL3Renderer
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
platformIO.RendererCreateWindow = (void*)Marshal.GetFunctionPointerForDelegate(RendererCreateWindowDelegate);
platformIO.RendererDestroyWindow = (void*)Marshal.GetFunctionPointerForDelegate(RendererDestroyWindowDelegate);
platformIO.RendererSetWindowSize = (void*)Marshal.GetFunctionPointerForDelegate(RendererSetWindowSizeDelegate);
platformIO.RendererRenderWindow = (void*)Marshal.GetFunctionPointerForDelegate(RendererRenderWindowDelegate);
platformIO.RendererSwapBuffers = (void*)Marshal.GetFunctionPointerForDelegate(RendererSwapBuffersDelegate);
@@ -108,22 +99,19 @@ public unsafe static class ImGuiSDL3Renderer
private static void RendererCreateWindow(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData<ImGuiSDL3Platform.ViewPortData>.Get(viewport.PlatformUserData);
if (vd == null || vd.Window == nint.Zero)
return;
if (vd.Renderer == nint.Zero)
{
vd.Renderer = SDL.CreateRenderer(vd.Window, (string?)null);
vd.Renderer = SDL.CreateRenderer(vd.Window, null);
vd.RendererOwned = true;
}
Program.Logger.Log(null);
}
private static void RendererDestroyWindow(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData<ImGuiSDL3Platform.ViewPortData>.Get(viewport.PlatformUserData);
if (vd == null)
return;
@@ -135,37 +123,32 @@ public unsafe static class ImGuiSDL3Renderer
vd.Renderer = nint.Zero;
vd.RendererOwned = false;
Program.Logger.Log(null);
}
private static void RendererSetWindowSize(ImGuiViewportPtr viewport, Vector2 size)
{
// SDL renderer windows track size through the platform window callback.
Program.Logger.Log(null);
}
private static void RendererRenderWindow(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData<ImGuiSDL3Platform.ViewPortData>.Get(viewport.PlatformUserData);
if (vd == null || vd.Renderer == nint.Zero)
{
return;
}
if (Parent != null)
{
SDL.SetRenderDrawColorFloat(vd.Renderer, Parent.ClearColor.X, Parent.ClearColor.Y, Parent.ClearColor.Z, 0f);
SDL.RenderClear(vd.Renderer);
}
RenderDrawData(viewport.DrawData, vd.Renderer);
Program.Logger.Log(null);
}
private static void RendererSwapBuffers(ImGuiViewportPtr viewport)
{
Program.Logger.Log(null);
ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData<ImGuiSDL3Platform.ViewPortData>.Get(viewport.PlatformUserData);
if (vd == null || vd.Renderer == nint.Zero)
return;
SDL.RenderPresent(vd.Renderer);
Program.Logger.Log(null);
}
public static void RenderDrawData(ImDrawDataPtr drawData, nint renderer)
@@ -175,9 +158,7 @@ public unsafe static class ImGuiSDL3Renderer
return;
SDL.GetRenderScale(renderer, out float renderScaleX, out float renderScaleY);
Vector2 renderScale = new Vector2(
renderScaleX == 1.0f ? drawData.FramebufferScale.X : 1.0f,
renderScaleY == 1.0f ? drawData.FramebufferScale.Y : 1.0f);
Vector2 renderScale = new Vector2(renderScaleX == 1.0f ? drawData.FramebufferScale.X : 1.0f, renderScaleY == 1.0f ? drawData.FramebufferScale.Y : 1.0f);
int fbWidth = (int)(drawData.DisplaySize.X * renderScale.X);
int fbHeight = (int)(drawData.DisplaySize.Y * renderScale.Y);
@@ -196,13 +177,11 @@ public unsafe static class ImGuiSDL3Renderer
ViewportEnabled = SDL.RenderViewportSet(renderer),
ClipEnabled = SDL.RenderClipEnabled(renderer)
};
SDL.GetRenderViewport(renderer, out var oldViewport);
old.Viewport = oldViewport;
SDL.GetRenderClipRect(renderer, out var oldClipRect);
old.ClipRect = oldClipRect;
SDL.GetRenderViewport(renderer, out old.Viewport);
SDL.GetRenderClipRect(renderer, out old.ClipRect);
// Set up render state
SDL.SetRenderViewport(renderer, 0);
SDL.SetRenderViewport(renderer, nint.Zero);
SDL.SetRenderClipRect(renderer, nint.Zero);
// Set render state in platform IO
@@ -224,37 +203,35 @@ public unsafe static class ImGuiSDL3Renderer
{
continue; // User callback not implemented
}
else
// Apply clipping rectangle
Vector4 clipRect = cmd.ClipRect;
Vector2 clipMin = new Vector2((clipRect.X - clipOffset.X) * renderScale.X, (clipRect.Y - clipOffset.Y) * renderScale.Y);
Vector2 clipMax = new Vector2((clipRect.Z - clipOffset.X) * renderScale.X, (clipRect.W - clipOffset.Y) * renderScale.Y);
clipMin.X = Math.Max(0, clipMin.X);
clipMin.Y = Math.Max(0, clipMin.Y);
clipMax.X = Math.Min(fbWidth, clipMax.X);
clipMax.Y = Math.Min(fbHeight, clipMax.Y);
if (clipMax.X <= clipMin.X || clipMax.Y <= clipMin.Y)
continue;
SDL.Rect r = new SDL.Rect
{
// Apply clipping rectangle
Vector4 clipRect = cmd.ClipRect;
Vector2 clipMin = new Vector2((clipRect.X - clipOffset.X) * renderScale.X, (clipRect.Y - clipOffset.Y) * renderScale.Y);
Vector2 clipMax = new Vector2((clipRect.Z - clipOffset.X) * renderScale.X, (clipRect.W - clipOffset.Y) * renderScale.Y);
X = (int)clipMin.X,
Y = (int)clipMin.Y,
W = (int)(clipMax.X - clipMin.X),
H = (int)(clipMax.Y - clipMin.Y)
};
SDL.SetRenderClipRect(renderer, r);
clipMin.X = Math.Max(0, clipMin.X);
clipMin.Y = Math.Max(0, clipMin.Y);
clipMax.X = Math.Min(fbWidth, clipMax.X);
clipMax.Y = Math.Min(fbHeight, clipMax.Y);
if (clipMax.X <= clipMin.X || clipMax.Y <= clipMin.Y)
continue;
// Get texture
nint texId = cmd.GetTexID();
SDL.Rect r = new SDL.Rect
{
X = (int)clipMin.X,
Y = (int)clipMin.Y,
W = (int)(clipMax.X - clipMin.X),
H = (int)(clipMax.Y - clipMin.Y)
};
SDL.SetRenderClipRect(renderer, r);
// Get texture
nint texId = ResolveTextureId(cmd.GetTexID(), renderer);
// Convert ImGui vertices to SDL vertices
if (!RenderDrawCommand(cmdList, cmd, renderer, texId, renderScale))
{
Console.WriteLine($"Failed to render ImGui draw command: {SDL.GetError()}");
}
// Convert ImGui vertices to SDL vertices
if (!RenderDrawCommand(cmdList, cmd, renderer, texId, renderScale, clipOffset))
{
Console.WriteLine($"Failed to render ImGui draw command: {SDL.GetError()}");
}
}
}
@@ -267,108 +244,53 @@ public unsafe static class ImGuiSDL3Renderer
SDL.SetRenderClipRect(renderer, old.ClipEnabled ? old.ClipRect : new SDL.Rect());
}
private static nint ResolveTextureId(ImTextureID texId, nint renderer)
{
if (texId == ImTextureID.Null)
return nint.Zero;
TextureState? state = ImGuiUserData<TextureState>.Get((void*)(nint)texId);
if (state == null)
return (nint)texId;
if (!state.RendererTextures.TryGetValue(renderer, out nint rendererTexture) || rendererTexture == nint.Zero)
{
rendererTexture = CreateRendererTexture(state, renderer);
state.RendererTextures[renderer] = rendererTexture;
}
return rendererTexture;
}
private static nint CreateRendererTexture(TextureState state, nint renderer)
{
ImTextureDataPtr tex = state.Source;
// We keep ARGB8888 here because this project already relies on that upload path.
nint sdlTexture = SDL.CreateTexture(renderer, SDL.PixelFormat.ARGB8888, SDL.TextureAccess.Static, tex.Width, tex.Height);
if (sdlTexture == nint.Zero)
return nint.Zero;
SDL.UpdateTexture(sdlTexture, nint.Zero, (nint)tex.GetPixels(), tex.GetPitch());
SDL.SetTextureBlendMode(sdlTexture, SDL.BlendMode.Blend);
SDL.SetTextureScaleMode(sdlTexture, SDL.ScaleMode.Linear);
return sdlTexture;
}
private static void UploadRendererTexture(ImTextureDataPtr tex, nint renderer, nint sdlTexture)
{
if (tex.Status == ImTextureStatus.WantUpdates)
{
for (int i = 0; i < tex.Updates.Size; i++)
{
var r = tex.Updates[i];
SDL.Rect rect = new SDL.Rect
{
X = r.X,
Y = r.Y,
W = r.W,
H = r.H
};
SDL.UpdateTexture(sdlTexture, rect, (nint)tex.GetPixelsAt(r.X, r.Y), tex.GetPitch());
}
}
else
{
SDL.UpdateTexture(sdlTexture, nint.Zero, (nint)tex.GetPixels(), tex.GetPitch());
}
}
private static void UpdateTexture(ImTextureDataPtr tex, nint renderer)
{
TextureState? state = null;
if ((nint)tex.BackendUserData != nint.Zero)
state = ImGuiUserData<TextureState>.Get(tex.BackendUserData);
if (state == null)
{
state = new TextureState();
tex.BackendUserData = ImGuiUserData<TextureState>.Store(state);
}
state.Source = tex;
tex.SetTexID((nint)tex.BackendUserData);
if (tex.Status == ImTextureStatus.WantDestroy)
{
foreach (nint rendererTexture in state.RendererTextures.Values)
{
if (rendererTexture != nint.Zero)
SDL.DestroyTexture(rendererTexture);
}
if (tex.TexID != ImTextureID.Null)
SDL.DestroyTexture(tex.TexID);
state.RendererTextures.Clear();
ImGuiUserData<TextureState>.Free(tex.BackendUserData);
tex.BackendUserData = (void*)nint.Zero;
tex.SetTexID(ImTextureID.Null);
tex.SetStatus(ImTextureStatus.Destroyed);
return;
}
bool hasRendererTexture = state.RendererTextures.TryGetValue(renderer, out nint sdlTexture) && sdlTexture != nint.Zero;
if (!hasRendererTexture)
bool needsCreate = tex.TexID == ImTextureID.Null || SDL.GetRendererFromTexture(tex.TexID) != renderer;
if (tex.Status == ImTextureStatus.WantCreate || needsCreate)
{
sdlTexture = CreateRendererTexture(state, renderer);
state.RendererTextures[renderer] = sdlTexture;
if (tex.TexID != ImTextureID.Null)
SDL.DestroyTexture(tex.TexID);
// Create texture on the renderer that is actually drawing this viewport.
// SDL textures are renderer-owned and cannot be shared across renderer instances.
nint sdlTexture = SDL.CreateTexture(renderer, SDL.PixelFormat.ABGR8888, SDL.TextureAccess.Static, tex.Width, tex.Height);
SDL.UpdateTexture(sdlTexture, nint.Zero, (nint)tex.GetPixels(), tex.GetPitch());
SDL.SetTextureBlendMode(sdlTexture, SDL.BlendMode.Blend);
SDL.SetTextureScaleMode(sdlTexture, SDL.ScaleMode.Linear);
tex.SetTexID(sdlTexture);
tex.SetStatus(ImTextureStatus.Ok);
return;
}
if (sdlTexture != nint.Zero && (tex.Status == ImTextureStatus.WantCreate || tex.Status == ImTextureStatus.WantUpdates || !hasRendererTexture))
if (tex.Status == ImTextureStatus.WantUpdates)
{
UploadRendererTexture(tex, renderer, sdlTexture);
// Update selected blocks. We only ever write to textures regions which have never been used before!
// This backend choose to use tex.Updates[] but you can use tex.UpdateRect to upload a single region.
nint sdlTexture = tex.TexID;
for (int i = 0; i < tex.Updates.Size; i++)
{
var r = tex.Updates[i];
SDL.Rect sdlR = new SDL.Rect { X = r.X, Y = r.Y, W = r.W, H = r.H };
SDL.UpdateTexture(sdlTexture, sdlR, (nint)tex.GetPixelsAt(r.X, r.Y), tex.GetPitch());
}
tex.SetStatus(ImTextureStatus.Ok);
}
}
private static bool RenderDrawCommand(ImDrawListPtr drawList, ImDrawCmd cmd, nint renderer, nint texId, Vector2 scale)
private static bool RenderDrawCommand(ImDrawListPtr drawList, ImDrawCmd cmd, nint renderer, nint texId, Vector2 scale, Vector2 displayPos)
{
uint indexOffset = cmd.IdxOffset;
uint vertexOffset = cmd.VtxOffset;
@@ -391,21 +313,21 @@ public unsafe static class ImGuiSDL3Renderer
byte b = (byte)((col >> 16) & 0xFF);
byte a = (byte)((col >> 24) & 0xFF);
vertices[i] = new SDL.Vertex()
vertices[i] = new SDL.Vertex
{
Position = new SDL.FPoint()
Position = new SDL.FPoint
{
X = srcVert.Pos.X * scale.X,
Y = srcVert.Pos.Y * scale.Y
X = (srcVert.Pos.X - displayPos.X) * scale.X,
Y = (srcVert.Pos.Y - displayPos.Y) * scale.Y
},
Color = new SDL.FColor()
Color = new SDL.FColor
{
R = r / 255f,
G = g / 255f,
B = b / 255f,
A = a / 255f
},
TexCoord = new SDL.FPoint()
TexCoord = new SDL.FPoint
{
X = srcVert.Uv.X,
Y = srcVert.Uv.Y
@@ -427,7 +349,7 @@ public unsafe static class ImGuiSDL3Renderer
{
var texture = texures[i];
texture.Status = ImTextureStatus.WantDestroy;
UpdateTexture(texture, nint.Zero);
UpdateTexture(texture, Data.Renderer);
}
}
}
}
+4 -8
View File
@@ -1,12 +1,8 @@
using System;
using System.Diagnostics;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using Hexa.NET.ImGui;
using Hexa.NET.ImGui.Utilities;
using Hexa.NET.ImGuizmo;
using Hexa.NET.ImPlot;
using Hexa.NET.ImPlot3D;
using SDL3;
namespace SDL3_TestingSuite.SDL3;
@@ -54,7 +50,7 @@ public sealed unsafe class SDL3Window : IDisposable
ImGuiIOPtr io = ImGui.GetIO();
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.NavEnableGamepad | ImGuiConfigFlags.DockingEnable;
// io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines;
io.Fonts.AddFontDefault();
@@ -96,8 +92,8 @@ public sealed unsafe class SDL3Window : IDisposable
}
// Init platform and renderer
ImGuiSDL3Platform.Init(Window, Renderer);
ImGuiSDL3Renderer.Init(Renderer);
ImGuiSDL3Platform.Init(Window, Renderer, this);
ImGuiSDL3Renderer.Init(Renderer, this);
}
public void Run()