From c9ccaad2ed2b9bdf9372a1836460659c4e16d93f Mon Sep 17 00:00:00 2001 From: Grim Maple Date: Tue, 26 Jul 2022 21:54:25 +0300 Subject: [PATCH] Add multisampling support --- examples/opengl/src/openglexample.d | 6 + src/dlangui/graphics/glsupport.d | 3 +- src/dlangui/platforms/common/platform.d | 19 ++- src/dlangui/platforms/windows/winapp.d | 183 +++++++++++++++++++++--- 4 files changed, 186 insertions(+), 25 deletions(-) diff --git a/examples/opengl/src/openglexample.d b/examples/opengl/src/openglexample.d index 759a39cb..09f3a3fc 100644 --- a/examples/opengl/src/openglexample.d +++ b/examples/opengl/src/openglexample.d @@ -15,6 +15,9 @@ extern (C) int UIAppMain(string[] args) { Platform.instance.GLVersionMajor = 2; Platform.instance.GLVersionMinor = 1; + // Enable multisampling + Platform.instance.multisamples = 16; + // create window Window window = Platform.instance.createWindow("DlangUI OpenGL Example", null, WindowFlag.Resizable, 800, 700); @@ -193,6 +196,9 @@ class MyOpenglWidget : VerticalLayout { return; } + checkgl!glEnable(GL_MULTISAMPLE); + checkgl!glEnable(GL_POLYGON_SMOOTH); + checkgl!glEnable(GL_CULL_FACE); checkgl!glEnable(GL_DEPTH_TEST); checkgl!glCullFace(GL_BACK); diff --git a/src/dlangui/graphics/glsupport.d b/src/dlangui/graphics/glsupport.d index e71f0f47..0478562d 100644 --- a/src/dlangui/graphics/glsupport.d +++ b/src/dlangui/graphics/glsupport.d @@ -937,7 +937,8 @@ final class GLSupport { /// call glFlush void flushGL() { - checkgl!glFlush(); + // TODO: Is this really needed? + // checkgl!glFlush(); } bool generateMipmap(int dx, int dy, ubyte * pixels, int level, ref ubyte[] dst) { diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 87909009..bb03d4d0 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -280,8 +280,8 @@ class Window : CustomEventTarget { return; WindowState state = windowState; Rect rect = windowRect; - if (state == WindowState.fullscreen - || state == WindowState.minimized + if (state == WindowState.fullscreen + || state == WindowState.minimized || state == WindowState.maximized || state == WindowState.normal) { // @@ -310,7 +310,7 @@ class Window : CustomEventTarget { rect.right = w; rect.bottom = h; if (correctWindowPositionOnScreen(rect) && (state == WindowState.fullscreen - || state == WindowState.minimized + || state == WindowState.minimized || state == WindowState.maximized || state == WindowState.normal)) { setWindowState(state, false, rect); @@ -1124,7 +1124,7 @@ class Window : CustomEventTarget { @property CursorType overrideCursorType() { return _overrideCursorType; } - + protected bool dispatchMouseEvent(Widget root, MouseEvent event, ref bool cursorIsSet) { // only route mouse events to visible widgets if (root.visibility != Visibility.Visible) @@ -1483,12 +1483,12 @@ class Window : CustomEventTarget { if (event.action == MouseAction.Move || event.action == MouseAction.Leave) { processed = checkRemoveTracking(event); } - + bool cursorIsSet = false; if (overrideCursorType != CursorType.NotSet) { cursorIsSet = true; } - + if (!res) { bool insideOneOfPopups = false; for (int i = cast(int)_popups.length - 1; i >= 0; i--) { @@ -1843,6 +1843,13 @@ class Platform { * Note: if the version is invalid or not supported, this value will be set to supported one. */ int GLVersionMinor = 2; + + /** + * OpenGL multisamples amount. + * Note: 0, 2, 4, 8, or 16 are accepted. + * TODO: Not supported on Linux + */ + int multisamples = 0; } /** * close window diff --git a/src/dlangui/platforms/windows/winapp.d b/src/dlangui/platforms/windows/winapp.d index d5af722e..9a355848 100644 --- a/src/dlangui/platforms/windows/winapp.d +++ b/src/dlangui/platforms/windows/winapp.d @@ -59,11 +59,58 @@ extern (C) int UIAppMain(string[] args); immutable WIN_CLASS_NAME = "DLANGUI_APP"; +/* This is a pretty dirty hack to get multisampling to work */ +private __gshared bool isInitialized = false; + __gshared HINSTANCE _hInstance; __gshared int _cmdShow; static if (ENABLE_OPENGL) { - bool setupPixelFormat(HDC hDC) + + // WGL stuff + + // WGL_ARB_pixel_format + enum WGL_DRAW_TO_WINDOW_ARB = 0x2001; + enum WGL_DRAW_TO_BITMAP_ARB = 0x2002; + enum WGL_ACCELERATION_ARB = 0x2003; + enum WGL_SUPPORT_GDI_ARB = 0x200F; + enum WGL_SUPPORT_OPENGL_ARB = 0x2010; + enum WGL_DOUBLE_BUFFER_ARB = 0x2011; + enum WGL_STEREO_ARB = 0x2012; + enum WGL_PIXEL_TYPE_ARB = 0x2013; + enum WGL_COLOR_BITS_ARB = 0x2014; + enum WGL_DEPTH_BITS_ARB = 0x2022; + enum WGL_STENCIL_BITS_ARB = 0x2023; + + enum WGL_NO_ACCELERATION_ARB = 0x2025; + enum WGL_GENERIC_ACCELERATION_ARB = 0x2026; + enum WGL_FULL_ACCELERATION_ARB = 0x2027; + + enum WGL_TYPE_RGBA_ARB = 0x202B; + enum WGL_TYPE_COLORINDEX_ARB = 0x202C; + + // WGL_ARB_create_context_profile + enum WGL_CONTEXT_MAJOR_VERSION_ARB = 0x2091; + enum WGL_CONTEXT_MINOR_VERSION_ARB = 0x2092; + enum WGL_CONTEXT_FLAGS_ARB = 0x2094; + enum WGL_CONTEXT_PROFILE_MASK_ARB = 0x9126; + + // WGL_CONTEXT_FLAGS bits + enum WGL_CONTEXT_DEBUG_BIT_ARB = 0x0001; + enum WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB = 0x0002; + + // WGL_CONTEXT_PROFILE_MASK_ARB bits + enum WGL_CONTEXT_CORE_PROFILE_BIT_ARB = 0x00000001; + enum WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB = 0x00000002; + + enum GL_NUM_EXTENSIONS = 0x821D; + + enum WGL_ALPHA_BITS_ARB = 0x201B; + + enum WGL_SAMPLE_BUFFERS_ARB = 0x2041; + enum WGL_SAMPLES_ARB = 0x2042; + + bool setupPixelFormat(HDC hDC, int multisamples = 0) { PIXELFORMATDESCRIPTOR pfd = { PIXELFORMATDESCRIPTOR.sizeof, /* size */ @@ -72,7 +119,7 @@ static if (ENABLE_OPENGL) { PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER, /* support double-buffering */ PFD_TYPE_RGBA, /* color type */ - 16, /* prefered color depth */ + 24, /* prefered color depth */ 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ 0, /* no alpha buffer */ 0, /* alpha bits (ignored) */ @@ -87,7 +134,7 @@ static if (ENABLE_OPENGL) { }; int pixelFormat; - pixelFormat = ChoosePixelFormat(hDC, &pfd); + pixelFormat = multisamples > 0 ? sharedGLContext.multisampleFormat(hDC, multisamples) : ChoosePixelFormat(hDC, &pfd); if (pixelFormat == 0) { Log.e("ChoosePixelFormat failed."); return false; @@ -158,6 +205,9 @@ const uint CUSTOM_MESSAGE_ID = WM_USER + 1; static if (ENABLE_OPENGL) { +alias BOOL function(HDC hdc, const(int)* attributes, const(FLOAT)* fAttributes, UINT maxFormats, int* pixelFormat, UINT *numFormats) PFNWGLCHOOSEPIXELFORMATARBPROC; +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; + /// Shared opengl context helper struct SharedGLContext { import bindbc.opengl; @@ -183,14 +233,8 @@ static if (ENABLE_OPENGL) { _hGLRC = wglCreateContext(hDC); if (_hGLRC) { bind(hDC); - bool initialized = initGLSupport(Platform.instance.GLVersionMajor < 3); + wglChoosePixelFormatARB = cast(PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); unbind(hDC); - if (!initialized) { - uninit(); - Log.e("Failed to init OpenGL shaders"); - _error = true; - return false; - } return true; } else { _error = true; @@ -202,6 +246,45 @@ static if (ENABLE_OPENGL) { return false; } } + + bool initGLBindings(HDC hDC) + { + bind(hDC); + scope(exit) unbind(hDC); + bool initialized = initGLSupport(Platform.instance.GLVersionMajor < 3); + if (!initialized) { + uninit(); + Log.e("Failed to init OpenGL shaders"); + _error = true; + return false; + } + return true; + } + + /// A helper function to reinit a context to use multisampling + bool reinit(HDC hDC, int samples) + { + if(setupPixelFormat(hDC, samples)) + { + _hPalette = setupPalette(hDC); + _hGLRC = wglCreateContext(hDC); + if (_hGLRC) { + return true; + } + else + { + _error = true; + return false; + } + } + else + { + Log.e("Cannot reinit pixel format"); + _error = true; + return false; + } + } + void uninit() { if (_hGLRC) { wglDeleteContext(_hGLRC); @@ -223,6 +306,42 @@ static if (ENABLE_OPENGL) { void swapBuffers(HDC hDC) { SwapBuffers(hDC); } + + int multisampleFormat(HDC hdc, int samples) + { + bind(hdc); + GLint pixelFormat; + BOOL valid; + GLuint numFormats; + + float[] fattribs = [0.0f, 0.0f]; + + int[] attribs = + [ + WGL_DRAW_TO_WINDOW_ARB,GL_TRUE, + WGL_SUPPORT_OPENGL_ARB,GL_TRUE, + WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB, + WGL_COLOR_BITS_ARB,24, + WGL_ALPHA_BITS_ARB,8, + WGL_DEPTH_BITS_ARB,16, + WGL_STENCIL_BITS_ARB,0, + WGL_DOUBLE_BUFFER_ARB,GL_TRUE, + WGL_SAMPLE_BUFFERS_ARB,GL_TRUE, + WGL_SAMPLES_ARB, samples, + WGL_CONTEXT_MAJOR_VERSION_ARB, Platform.instance.GLVersionMajor, + WGL_CONTEXT_MINOR_VERSION_ARB, Platform.instance.GLVersionMinor, + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + 0 + ]; + + valid = wglChoosePixelFormatARB(hdc, attribs.ptr, fattribs.ptr, 1, &pixelFormat, &numFormats); + if(!valid) + Log.e("wglMakeCurrent is failed. GetLastError=%x".format(GetLastError())); + + unbind(hdc); + return valid; + } } /// OpenGL context to share between windows @@ -297,7 +416,7 @@ class Win32Window : Window { parenthwnd, // parent window handle null, // window menu handle _hInstance, // program instance handle - cast(void*)this); // creation parameters + platform.multisamples ? cast(void*)null : cast(void*)this); // creation parameters static if (ENABLE_OPENGL) { /* initialize OpenGL rendering */ HDC hDC = GetDC(_hwnd); @@ -305,8 +424,34 @@ class Win32Window : Window { if (openglEnabled) { useOpengl = sharedGLContext.init(hDC); } + + if(platform.multisamples != 0) + { + sharedGLContext.uninit(); + ReleaseDC(_hwnd, hDC); + + // Recreate window with multisampling (copy-paste from above) + DestroyWindow(_hwnd); + _hwnd = CreateWindowW(toUTF16z(WIN_CLASS_NAME), // window class name + toUTF16z(windowCaption), // window caption + ws, // window style + x, // initial x position + y, // initial y position + _dx, // initial x size + _dy, // initial y size + parenthwnd, // parent window handle + null, // window menu handle + _hInstance, // program instance handle + cast(void*)this); // creation parameters + + hDC = GetDC(_hwnd); + useOpengl = sharedGLContext.reinit(hDC, platform.multisamples); + } } + sharedGLContext.initGLBindings(hDC); + isInitialized = true; + RECT rect; GetWindowRect(_hwnd, &rect); handleWindowStateChange(WindowState.unspecified, Rect(rect.left, rect.top, _dx, _dy)); @@ -322,7 +467,6 @@ class Win32Window : Window { HDC hdc2 = BeginPaint(_hwnd, &ps); EndPaint(_hwnd, &ps); - import bindbc.opengl; //3.gl3; import bindbc.opengl; //3.wgl; import dlangui.graphics.gldrawbuf; @@ -360,7 +504,7 @@ class Win32Window : Window { } buf.afterDrawing(); sharedGLContext.swapBuffers(hdc); - //sharedGLContext.unbind(hdc); + sharedGLContext.unbind(hdc); destroy(buf); } } @@ -1487,10 +1631,13 @@ LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { CREATESTRUCT * pcreateStruct = cast(CREATESTRUCT*)lParam; window = cast(Win32Window)pcreateStruct.lpCreateParams; - void * ptr = cast(void*) window; - SetWindowLongPtr(hwnd, GWLP_USERDATA, cast(LONG_PTR)ptr); - window._hwnd = hwnd; - window.onCreate(); + if(window !is null) + { + void * ptr = cast(void*) window; + SetWindowLongPtr(hwnd, GWLP_USERDATA, cast(LONG_PTR)ptr); + window._hwnd = hwnd; + window.onCreate(); + } //window.handleUnknownWindowMessage(message, wParam, lParam); } return 0; @@ -1499,7 +1646,7 @@ LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) //window.handleUnknownWindowMessage(message, wParam, lParam); window.onDestroy(); } - if (w32platform.windowCount == 0) + if (w32platform.windowCount == 0 && isInitialized) PostQuitMessage(0); return 0; case WM_WINDOWPOSCHANGED: