diff --git a/src/glcontext_egl.cpp b/src/glcontext_egl.cpp index 6fc3db41c..9d77ea573 100644 --- a/src/glcontext_egl.cpp +++ b/src/glcontext_egl.cpp @@ -1116,10 +1116,22 @@ WL_EGL_IMPORT bx::deleteObject(g_allocator, _swapChain); } - void GlContext::swap(SwapChainGL* _swapChain) + void GlContext::swap(SwapChainGL* _swapChain, bool _vsync) { makeCurrent(_swapChain); + const bool noWait = true + && !_vsync + && 0 != m_swapInterval + && NULL != m_display + && (NULL != _swapChain || EGL_NO_SURFACE != m_surface) + ; + + if (noWait) + { + eglSwapInterval(m_display, 0); + } + if (NULL == _swapChain) { if (NULL != m_display @@ -1132,6 +1144,11 @@ WL_EGL_IMPORT { _swapChain->swapBuffers(); } + + if (noWait) + { + eglSwapInterval(m_display, m_swapInterval); + } } void GlContext::makeCurrent(SwapChainGL* _swapChain) diff --git a/src/glcontext_egl.h b/src/glcontext_egl.h index 6e4d855c2..be7044614 100644 --- a/src/glcontext_egl.h +++ b/src/glcontext_egl.h @@ -64,7 +64,7 @@ namespace bgfx { namespace gl uint64_t getCaps() const; SwapChainGL* createSwapChain(void* _nwh, int32_t _width, int32_t _height); void destroySwapChain(SwapChainGL* _swapChain); - void swap(SwapChainGL* _swapChain = NULL); + void swap(SwapChainGL* _swapChain = NULL, bool _vsync = true); void makeCurrent(SwapChainGL* _swapChain = NULL); bool import(); diff --git a/src/glcontext_html5.cpp b/src/glcontext_html5.cpp index 67f46638d..2d1c06700 100644 --- a/src/glcontext_html5.cpp +++ b/src/glcontext_html5.cpp @@ -193,7 +193,7 @@ namespace bgfx { namespace gl bx::deleteObject(g_allocator, _swapChain); } - void GlContext::swap(SwapChainGL* /* _swapChain */) + void GlContext::swap(SwapChainGL* /* _swapChain */, bool /* _vsync */) { } diff --git a/src/glcontext_html5.h b/src/glcontext_html5.h index ff1329c17..5a342d900 100644 --- a/src/glcontext_html5.h +++ b/src/glcontext_html5.h @@ -28,7 +28,7 @@ namespace bgfx { namespace gl uint64_t getCaps() const; SwapChainGL* createSwapChain(void* _nwh, int32_t _width, int32_t _height); void destroySwapChain(SwapChainGL* _swapChain); - void swap(SwapChainGL* _swapChain = NULL); + void swap(SwapChainGL* _swapChain = NULL, bool _vsync = true); void makeCurrent(SwapChainGL* _swapChain = NULL); bool import(); diff --git a/src/glcontext_wgl.cpp b/src/glcontext_wgl.cpp index ce2d8430f..7c35988c6 100644 --- a/src/glcontext_wgl.cpp +++ b/src/glcontext_wgl.cpp @@ -28,6 +28,7 @@ namespace bgfx { namespace gl PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; + PFNDWMFLUSHPROC DwmFlush; # define GL_IMPORT(_optional, _proto, _func, _import) _proto _func # include "glimports.h" @@ -50,7 +51,6 @@ namespace bgfx { namespace gl ~SwapChainGL() { wglMakeCurrent(NULL, NULL); - wglDeleteContext(m_context); ReleaseDC(m_hwnd, m_hdc); } @@ -200,6 +200,13 @@ namespace bgfx { namespace gl goto error; } + m_dwmapidll = bx::dlopen("dwmapi.dll"); + + if (NULL != m_dwmapidll) + { + DwmFlush = bx::dlsym(m_dwmapidll, "DwmFlush"); + } + // If g_platformHooks.nwh is NULL, the assumption is that GL context was created // by user (for example, using SDL, GLFW, etc.) BX_WARN(NULL != _swapChain.nwh @@ -295,19 +302,6 @@ namespace bgfx { namespace gl dumpExtensions(extensions); } - // Attributes for the contexts SwapChainGL shares with this one. - const int32_t contextAttrs[9] = - { - WGL_CONTEXT_MAJOR_VERSION_ARB, BGFX_WGL_CONTEXT_VERSION / 10, - WGL_CONTEXT_MINOR_VERSION_ARB, BGFX_WGL_CONTEXT_VERSION % 10, - WGL_CONTEXT_FLAGS_ARB, BGFX_CONFIG_DEBUG ? WGL_CONTEXT_DEBUG_BIT_ARB : 0, - WGL_CONTEXT_PROFILE_MASK_ARB, BGFX_WGL_CONTEXT_PROFILE_BIT, - 0 - }; - - static_assert(sizeof(contextAttrs) == sizeof(m_contextAttrs) ); - bx::memCopy(m_contextAttrs, contextAttrs, sizeof(contextAttrs) ); - m_current = NULL; m_swapInterval = !!(_reset & BGFX_RESET_VSYNC) ? 1 : 0; @@ -463,9 +457,6 @@ namespace bgfx { namespace gl goto error; } - - static_assert(sizeof(contextAttrs) == sizeof(m_contextAttrs) ); - bx::memCopy(m_contextAttrs, contextAttrs, sizeof(contextAttrs) ); } wglMakeCurrent(NULL, NULL); @@ -530,6 +521,10 @@ namespace bgfx { namespace gl m_dummyHwnd = NULL; } + bx::dlclose(m_dwmapidll); + m_dwmapidll = NULL; + DwmFlush = NULL; + bx::dlclose(m_opengl32dll); m_opengl32dll = NULL; [[fallthrough]]; @@ -569,6 +564,10 @@ namespace bgfx { namespace gl m_dummyHwnd = NULL; } + bx::dlclose(m_dwmapidll); + m_dwmapidll = NULL; + DwmFlush = NULL; + bx::dlclose(m_opengl32dll); m_opengl32dll = NULL; } @@ -601,10 +600,7 @@ namespace bgfx { namespace gl if (NULL != wglSwapIntervalEXT) { - // Apply to the currently-bound (main) context. Secondary SwapChainGL contexts - // get the value applied lazily in makeCurrent() when they become current, since - // wglSwapIntervalEXT is per-context on Windows. - wglSwapIntervalEXT(m_swapInterval); + wglSwapIntervalEXT(swapInterval() ); } } @@ -632,18 +628,32 @@ namespace bgfx { namespace gl int result = SetPixelFormat(swapChain->m_hdc, m_pixelFormat, &m_pfd); BX_WARN(result, "SetPixelFormat failed (last err: 0x%08x)!", GetLastError() ); BX_UNUSED(result); - swapChain->m_context = wglCreateContextAttribsARB(swapChain->m_hdc, m_context, m_contextAttrs); - BX_ASSERT(NULL != swapChain->m_context, "Create swap chain failed: %x", glGetError() ); + swapChain->m_context = m_context; + ++m_numSwapChains; + + if (NULL != wglSwapIntervalEXT) + { + wglSwapIntervalEXT(swapInterval() ); + } + return swapChain; } void GlContext::destroySwapChain(SwapChainGL* _swapChain) { bx::deleteObject(g_allocator, _swapChain); + --m_numSwapChains; + wglMakeCurrent(m_hdc, m_context); + m_current = NULL; + + if (NULL != wglSwapIntervalEXT) + { + wglSwapIntervalEXT(swapInterval() ); + } } - void GlContext::swap(SwapChainGL* _swapChain) + void GlContext::swap(SwapChainGL* _swapChain, bool _vsync) { makeCurrent(_swapChain); @@ -658,6 +668,14 @@ namespace bgfx { namespace gl { _swapChain->swapBuffers(); } + + if (true + && _vsync + && 0 != m_swapInterval + && 0 == swapInterval() ) + { + DwmFlush(); + } } void GlContext::makeCurrent(SwapChainGL* _swapChain) @@ -677,16 +695,18 @@ namespace bgfx { namespace gl _swapChain->makeCurrent(); } - // wglSwapIntervalEXT is per-context on Windows, so re-apply the cached interval - // every time a different context becomes current. Without this, secondary swap - // chains keep their driver default (typically vsync ON) even after resize(). if (NULL != wglSwapIntervalEXT) { - wglSwapIntervalEXT(m_swapInterval); + wglSwapIntervalEXT(swapInterval() ); } } } + int GlContext::swapInterval() const + { + return 0 != m_numSwapChains && NULL != DwmFlush ? 0 : m_swapInterval; + } + bool GlContext::import() { BX_TRACE("Import:"); diff --git a/src/glcontext_wgl.h b/src/glcontext_wgl.h index 86d7707b7..6155a0a56 100644 --- a/src/glcontext_wgl.h +++ b/src/glcontext_wgl.h @@ -24,6 +24,7 @@ typedef PROC (APIENTRYP PFNWGLGETPROCADDRESSPROC) (LPCSTR lpszProc); typedef BOOL (APIENTRYP PFNWGLMAKECURRENTPROC) (HDC hdc, HGLRC hglrc); typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTPROC) (HDC hdc); typedef BOOL (APIENTRYP PFNWGLDELETECONTEXTPROC) (HGLRC hglrc); +typedef HRESULT (WINAPI* PFNDWMFLUSHPROC) (void); // typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); @@ -69,10 +70,10 @@ typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum z struct GlContext { GlContext() - : m_contextAttrs() - , m_pfd() + : m_pfd() , m_current(NULL) , m_opengl32dll(NULL) + , m_dwmapidll(NULL) , m_nwh(NULL) , m_context(NULL) , m_hdc(NULL) @@ -80,6 +81,7 @@ typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum z , m_ownsContext(false) , m_msaaContext(false) , m_swapInterval(0) + , m_numSwapChains(0) , m_pixelFormat(0) { } @@ -91,8 +93,9 @@ typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum z uint64_t getCaps() const; SwapChainGL* createSwapChain(void* _nwh, int32_t _width, int32_t _height); void destroySwapChain(SwapChainGL* _swapChain); - void swap(SwapChainGL* _swapChain = NULL); + void swap(SwapChainGL* _swapChain = NULL, bool _vsync = true); void makeCurrent(SwapChainGL* _swapChain = NULL); + int swapInterval() const; bool import(); @@ -101,10 +104,10 @@ typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum z return NULL != m_context; } - int32_t m_contextAttrs[9]; PIXELFORMATDESCRIPTOR m_pfd; SwapChainGL* m_current; void* m_opengl32dll; + void* m_dwmapidll; void* m_nwh; HGLRC m_context; HDC m_hdc; @@ -112,6 +115,7 @@ typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum z bool m_ownsContext; bool m_msaaContext; int m_swapInterval; + int m_numSwapChains; int m_pixelFormat; }; } /* namespace gl */ } // namespace bgfx diff --git a/src/renderer_gl.cpp b/src/renderer_gl.cpp index a5700da8d..ee067579c 100644 --- a/src/renderer_gl.cpp +++ b/src/renderer_gl.cpp @@ -3233,6 +3233,19 @@ namespace bgfx { namespace gl { if (m_flip) { + uint32_t lastPresent = 0; + + if (!m_needPresent) + { + for (uint32_t ii = 1, num = m_numWindows; ii < num; ++ii) + { + if (m_frameBuffers[m_windows[ii].idx].m_needPresent) + { + lastPresent = ii; + } + } + } + for (uint32_t ii = 1, num = m_numWindows; ii < num; ++ii) { FrameBufferGL& frameBuffer = m_frameBuffers[m_windows[ii].idx]; @@ -3244,7 +3257,7 @@ namespace bgfx { namespace gl frameBuffer.blitSwapChainFbo(); } - m_glctx.swap(frameBuffer.m_swapChain); + m_glctx.swap(frameBuffer.m_swapChain, lastPresent == ii); frameBuffer.m_needPresent = false; } } @@ -8641,11 +8654,6 @@ namespace bgfx { namespace gl static void blitBufferToTexture(const TextureGL& _dst, GLuint _src, const BlitItem& _bi) { - if (NULL == glBindBuffer) - { - return; - } - if (NULL != glMemoryBarrier) { GL_CHECK(glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT) ); @@ -8713,11 +8721,6 @@ namespace bgfx { namespace gl static void blitTextureToBuffer(GLuint _dst, const TextureGL& _src, const BlitItem& _bi) { - if (NULL == glBindBuffer) - { - return; - } - if (NULL == glGetTextureSubImage) { BX_WARN(false, "Blit texture into buffer is not supported. glGetTextureSubImage is unavailable."); diff --git a/src/renderer_vk.cpp b/src/renderer_vk.cpp index c1f425abc..766da228b 100644 --- a/src/renderer_vk.cpp +++ b/src/renderer_vk.cpp @@ -9110,7 +9110,18 @@ VK_DESTROY return; } - VK_CHECK(createSwapChain() ); + result = createSwapChain(); + + if (VK_SUCCESS != result) + { + releaseSwapChain(); + + m_sci.oldSwapchain = VK_NULL_HANDLE; + s_renderVK->kick(true); + + m_needToRecreateSwapchain = true; + return; + } } VK_CHECK(createAttachments(_commandBuffer) ); @@ -9662,6 +9673,7 @@ VK_DESTROY } release(m_swapChain); + m_numSwapChainImages = 0; } VkResult SwapChainVK::createAttachments(VkCommandBuffer _commandBuffer) @@ -10077,8 +10089,11 @@ VK_DESTROY m_needToRecreateSwapchain = true; return false; - case VK_ERROR_OUT_OF_DATE_KHR: case VK_SUBOPTIMAL_KHR: + m_needToRecreateSwapchain = !BX_ENABLED(BX_PLATFORM_ANDROID); + break; + + case VK_ERROR_OUT_OF_DATE_KHR: m_needToRecreateSwapchain = true; return false; @@ -10189,8 +10204,11 @@ VK_DESTROY m_needToRecreateSwapchain = true; break; - case VK_ERROR_OUT_OF_DATE_KHR: case VK_SUBOPTIMAL_KHR: + m_needToRecreateSwapchain = !BX_ENABLED(BX_PLATFORM_ANDROID); + break; + + case VK_ERROR_OUT_OF_DATE_KHR: m_needToRecreateSwapchain = true; break;