Created
November 15, 2025 19:32
-
-
Save pjmagee/af673fd5d3b54926493d81bf9dcdae58 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| using System.Runtime.InteropServices; | |
| using Silk.NET.Core.Native; | |
| using Silk.NET.Direct2D; | |
| using Silk.NET.Maths; | |
| using Silk.NET.DXGI; | |
| using D2DAlphaMode = Silk.NET.Direct2D.AlphaMode; | |
| using D2DFeatureLevel = Silk.NET.Direct2D.FeatureLevel; | |
| unsafe class Program | |
| { | |
| // Win32 API constants | |
| private const int WS_OVERLAPPEDWINDOW = 0x00CF0000; | |
| private const int WS_VISIBLE = 0x10000000; | |
| private const int CW_USEDEFAULT = unchecked((int)0x80000000); | |
| private const int SW_SHOW = 5; | |
| private const uint WM_DESTROY = 0x0002; | |
| private const uint WM_PAINT = 0x000F; | |
| private const uint WM_SIZE = 0x0005; | |
| private const uint CS_HREDRAW = 0x0002; | |
| private const uint CS_VREDRAW = 0x0001; | |
| private const uint IDC_ARROW = 32512; | |
| private const uint COLOR_WINDOW = 5; | |
| // Win32 API structures | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
| private struct WNDCLASSEXW | |
| { | |
| public uint cbSize; | |
| public uint style; | |
| public IntPtr lpfnWndProc; | |
| public int cbClsExtra; | |
| public int cbWndExtra; | |
| public IntPtr hInstance; | |
| public IntPtr hIcon; | |
| public IntPtr hCursor; | |
| public IntPtr hbrBackground; | |
| public IntPtr lpszMenuName; | |
| public IntPtr lpszClassName; | |
| public IntPtr hIconSm; | |
| } | |
| [StructLayout(LayoutKind.Sequential)] | |
| private struct MSG | |
| { | |
| public IntPtr hwnd; | |
| public uint message; | |
| public IntPtr wParam; | |
| public IntPtr lParam; | |
| public uint time; | |
| public POINT pt; | |
| } | |
| [StructLayout(LayoutKind.Sequential)] | |
| private struct POINT | |
| { | |
| public int X; | |
| public int Y; | |
| } | |
| [StructLayout(LayoutKind.Sequential)] | |
| private struct RECT | |
| { | |
| public int Left; | |
| public int Top; | |
| public int Right; | |
| public int Bottom; | |
| } | |
| [StructLayout(LayoutKind.Sequential)] | |
| private struct PAINTSTRUCT | |
| { | |
| public IntPtr hdc; | |
| public bool fErase; | |
| public RECT rcPaint; | |
| public bool fRestore; | |
| public bool fIncUpdate; | |
| [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 32)] | |
| public byte[] rgbReserved; | |
| } | |
| // Win32 API imports | |
| [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
| private static extern ushort RegisterClassExW(ref WNDCLASSEXW lpwcx); | |
| [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
| private static extern IntPtr CreateWindowExW( | |
| uint dwExStyle, | |
| IntPtr lpClassName, | |
| string lpWindowName, | |
| uint dwStyle, | |
| int x, int y, | |
| int nWidth, int nHeight, | |
| IntPtr hWndParent, | |
| IntPtr hMenu, | |
| IntPtr hInstance, | |
| IntPtr lpParam); | |
| [DllImport("user32.dll")] | |
| private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); | |
| [DllImport("user32.dll")] | |
| private static extern bool UpdateWindow(IntPtr hWnd); | |
| [DllImport("user32.dll")] | |
| private static extern sbyte GetMessageW(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); | |
| [DllImport("user32.dll")] | |
| private static extern bool TranslateMessage(ref MSG lpMsg); | |
| [DllImport("user32.dll")] | |
| private static extern IntPtr DispatchMessageW(ref MSG lpMsg); | |
| [DllImport("user32.dll")] | |
| private static extern void PostQuitMessage(int nExitCode); | |
| [DllImport("user32.dll")] | |
| private static extern IntPtr DefWindowProcW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); | |
| [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] | |
| private static extern IntPtr GetModuleHandleW(string? lpModuleName); | |
| [DllImport("user32.dll")] | |
| private static extern IntPtr LoadCursorW(IntPtr hInstance, IntPtr lpCursorName); | |
| [DllImport("user32.dll")] | |
| private static extern IntPtr BeginPaint(IntPtr hWnd, out PAINTSTRUCT lpPaint); | |
| [DllImport("user32.dll")] | |
| private static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT lpPaint); | |
| [DllImport("user32.dll")] | |
| private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); | |
| private delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); | |
| // Direct2D objects | |
| private static ID2D1Factory* d2dFactory; | |
| private static ID2D1HwndRenderTarget* renderTarget; | |
| private static ID2D1SolidColorBrush* brush; | |
| private static IntPtr hwnd; | |
| private static D2D d2d = null!; | |
| static void Main() | |
| { | |
| // Initialize Direct2D | |
| d2d = D2D.GetApi(); | |
| IntPtr hInstance = GetModuleHandleW(null); | |
| // Create window class name | |
| string className = "Direct2DWindowClass"; | |
| IntPtr classNamePtr = Marshal.StringToHGlobalUni(className); | |
| // Register window class | |
| WndProc wndProcDelegate = WindowProc; | |
| IntPtr wndProcPtr = Marshal.GetFunctionPointerForDelegate(wndProcDelegate); | |
| WNDCLASSEXW wc = new WNDCLASSEXW | |
| { | |
| cbSize = (uint)Marshal.SizeOf<WNDCLASSEXW>(), | |
| style = CS_HREDRAW | CS_VREDRAW, | |
| lpfnWndProc = wndProcPtr, | |
| cbClsExtra = 0, | |
| cbWndExtra = 0, | |
| hInstance = hInstance, | |
| hIcon = IntPtr.Zero, | |
| hCursor = LoadCursorW(IntPtr.Zero, new IntPtr(IDC_ARROW)), | |
| hbrBackground = new IntPtr(COLOR_WINDOW + 1), | |
| lpszMenuName = IntPtr.Zero, | |
| lpszClassName = classNamePtr, | |
| hIconSm = IntPtr.Zero | |
| }; | |
| if (RegisterClassExW(ref wc) == 0) | |
| { | |
| Console.WriteLine("Failed to register window class"); | |
| return; | |
| } | |
| // Create window (500x500) | |
| hwnd = CreateWindowExW( | |
| 0, | |
| classNamePtr, | |
| "Direct2D Simple Rendering", | |
| WS_OVERLAPPEDWINDOW | WS_VISIBLE, | |
| CW_USEDEFAULT, CW_USEDEFAULT, | |
| 500, 500, | |
| IntPtr.Zero, | |
| IntPtr.Zero, | |
| hInstance, | |
| IntPtr.Zero); | |
| if (hwnd == IntPtr.Zero) | |
| { | |
| Console.WriteLine("Failed to create window"); | |
| return; | |
| } | |
| // Initialize Direct2D factory | |
| InitializeDirect2D(); | |
| ShowWindow(hwnd, SW_SHOW); | |
| UpdateWindow(hwnd); | |
| // Message loop | |
| MSG msg; | |
| while (GetMessageW(out msg, IntPtr.Zero, 0, 0) > 0) | |
| { | |
| TranslateMessage(ref msg); | |
| DispatchMessageW(ref msg); | |
| } | |
| // Cleanup | |
| Cleanup(); | |
| Marshal.FreeHGlobal(classNamePtr); | |
| GC.KeepAlive(wndProcDelegate); | |
| } | |
| private static void InitializeDirect2D() | |
| { | |
| // Create D2D factory | |
| Guid iid = typeof(ID2D1Factory).GUID; | |
| fixed (ID2D1Factory** ppFactory = &d2dFactory) | |
| { | |
| d2d.D2D1CreateFactory(FactoryType.SingleThreaded, &iid, null, (void**)ppFactory); | |
| } | |
| } | |
| private static void CreateRenderTarget() | |
| { | |
| if (renderTarget != null) | |
| return; | |
| // Get client rect | |
| RECT rc; | |
| GetClientRect(hwnd, out rc); | |
| var size = new Silk.NET.Maths.Vector2D<uint>((uint)(rc.Right - rc.Left), (uint)(rc.Bottom - rc.Top)); | |
| var renderTargetProperties = new RenderTargetProperties | |
| { | |
| Type = RenderTargetType.Default, | |
| PixelFormat = new PixelFormat | |
| { | |
| Format = Format.FormatB8G8R8A8Unorm, | |
| AlphaMode = D2DAlphaMode.Premultiplied | |
| }, | |
| DpiX = 0, | |
| DpiY = 0, | |
| Usage = RenderTargetUsage.None, | |
| MinLevel = D2DFeatureLevel.Level10 | |
| }; | |
| var hwndRenderTargetProperties = new HwndRenderTargetProperties | |
| { | |
| Hwnd = new nint(hwnd), | |
| PixelSize = size, | |
| PresentOptions = PresentOptions.None | |
| }; | |
| fixed (ID2D1HwndRenderTarget** ppRenderTarget = &renderTarget) | |
| { | |
| d2dFactory->CreateHwndRenderTarget(&renderTargetProperties, &hwndRenderTargetProperties, ppRenderTarget); | |
| } | |
| // Create a solid color brush (blue) | |
| var color = new D3Dcolorvalue | |
| { | |
| R = 0.0f, | |
| G = 0.5f, | |
| B = 1.0f, | |
| A = 1.0f | |
| }; | |
| fixed (ID2D1SolidColorBrush** ppBrush = &brush) | |
| { | |
| ((ID2D1RenderTarget*)renderTarget)->CreateSolidColorBrush(&color, null, ppBrush); | |
| } | |
| } | |
| private static void Render() | |
| { | |
| if (renderTarget == null) | |
| CreateRenderTarget(); | |
| if (renderTarget == null) | |
| return; | |
| var baseRenderTarget = (ID2D1RenderTarget*)renderTarget; | |
| baseRenderTarget->BeginDraw(); | |
| // Clear background to white | |
| var clearColor = new D3Dcolorvalue | |
| { | |
| R = 1.0f, | |
| G = 1.0f, | |
| B = 1.0f, | |
| A = 1.0f | |
| }; | |
| baseRenderTarget->Clear(&clearColor); | |
| // Draw a simple square in the center (200x200 pixels) | |
| var rect = new Silk.NET.Maths.Box2D<float>(150, 150, 350, 350); | |
| baseRenderTarget->FillRectangle(&rect, (ID2D1Brush*)brush); | |
| ulong tag1, tag2; | |
| baseRenderTarget->EndDraw(&tag1, &tag2); | |
| } | |
| private static IntPtr WindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) | |
| { | |
| switch (msg) | |
| { | |
| case WM_PAINT: | |
| PAINTSTRUCT ps; | |
| BeginPaint(hWnd, out ps); | |
| Render(); | |
| EndPaint(hWnd, ref ps); | |
| return IntPtr.Zero; | |
| case WM_SIZE: | |
| if (renderTarget != null) | |
| { | |
| RECT rc; | |
| GetClientRect(hWnd, out rc); | |
| var size = new Silk.NET.Maths.Vector2D<uint>((uint)(rc.Right - rc.Left), (uint)(rc.Bottom - rc.Top)); | |
| renderTarget->Resize(&size); | |
| } | |
| return IntPtr.Zero; | |
| case WM_DESTROY: | |
| PostQuitMessage(0); | |
| return IntPtr.Zero; | |
| } | |
| return DefWindowProcW(hWnd, msg, wParam, lParam); | |
| } | |
| private static void Cleanup() | |
| { | |
| if (brush != null) | |
| { | |
| brush->Release(); | |
| brush = null; | |
| } | |
| if (renderTarget != null) | |
| { | |
| renderTarget->Release(); | |
| renderTarget = null; | |
| } | |
| if (d2dFactory != null) | |
| { | |
| d2dFactory->Release(); | |
| d2dFactory = null; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment