diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index ac9d3d68a..12b6523de 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1832,6 +1832,33 @@ typedef struct GLFWimage unsigned char* pixels; } GLFWimage; +/*! @brief Rectangle. + * + * This describes a single 2D box. The origin is the bottom-left point of the + * window. + * + * @sa @ref buffer_swap + * + * @since Added in version 3.4. + * + * @ingroup window + */ +typedef struct GLFWrect +{ + /*! The starting horizontal coordinate, in pixels, of this rect. + */ + int x; + /*! The starting vertical coordinate, in pixels, of this rect. + */ + int y; + /*! The width, in pixels, of this rect. + */ + int width; + /*! The height, in pixels, of this rect. + */ + int height; +} GLFWrect; + /*! @brief Gamepad input state * * This describes the input state of a gamepad. @@ -5647,6 +5674,7 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap + * @sa @ref glfwSwapBuffersWithDamage * @sa @ref glfwSwapInterval * * @since Added in version 1.0. @@ -5656,6 +5684,49 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); */ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); +/*! @brief Swaps the front and back buffers of the specified window with damage + * hints. + * + * This function swaps the front and back buffers of the specified window when + * rendering with OpenGL or OpenGL ES. If the swap interval is greater than + * zero, the GPU driver waits the specified number of screen updates before + * swapping the buffers. + * + * On supported platforms, this function can damage only the specified rects + * instead of the entire buffer. This is only one possible behaviour, it is + * perfectly acceptable to damage the entire buffer so you shouldn’t rely on + * that and keep providing a fully up to date buffer. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see `vkQueuePresentKHR` instead. + * + * @param[in] window The window whose buffers to swap. + * @param[in] rects The rects to update. + * @param[in] n_rects How many rects there are. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark __EGL:__ The context of the specified window must be current on the + * calling thread. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapBuffers + * @sa @ref glfwSwapInterval + * @sa @ref glfwGetBufferAge + * + * @since Added in version 3.4. + * + * @ingroup window + */ +GLFWAPI void glfwSwapBuffersWithDamage(GLFWwindow* window, GLFWrect* rects, int n_rects); + /*! @brief Sets the swap interval for the current context. * * This function sets the swap interval for the current OpenGL or OpenGL ES @@ -5702,6 +5773,36 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); */ GLFWAPI void glfwSwapInterval(int interval); +/*! @brief Returns the buffer age of the window’s current buffer. + * + * This function returns the age of the current buffer, in frames. It may be + * used to redraw only the parts of the buffer that have changed since this + * buffer was last used, thus avoiding a clear and draw of the entire buffer. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see the present mode of your swapchain instead. + * + * @param[in] window The window whose buffers to swap. + * @return The age of the back buffer if the extension is available, or 0 + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapBuffersWithDamage + * + * @since Added in version 3.4. + * + * @ingroup context + */ +GLFWAPI int glfwGetBufferAge(GLFWwindow* window); + /*! @brief Returns whether the specified extension is available. * * This function returns whether the specified diff --git a/src/context.c b/src/context.c index 48311e5fd..90c1c0945 100644 --- a/src/context.c +++ b/src/context.c @@ -657,6 +657,26 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) window->context.swapBuffers(window); } +GLFWAPI void glfwSwapBuffersWithDamage(GLFWwindow* handle, GLFWrect* rects, int n_rects) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot swap buffers of a window that has no OpenGL or OpenGL ES context"); + return; + } + + if (window->context.swapBuffersWithDamage) + window->context.swapBuffersWithDamage(window, rects, n_rects); + else + window->context.swapBuffers(window); +} + GLFWAPI void glfwSwapInterval(int interval) { _GLFWwindow* window; @@ -674,6 +694,26 @@ GLFWAPI void glfwSwapInterval(int interval) window->context.swapInterval(interval); } +GLFWAPI int glfwGetBufferAge(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, + "Cannot get buffer age of a window that has no OpenGL or OpenGL ES context"); + return 0; + } + + if (window->context.getBufferAge) + return window->context.getBufferAge(window); + + return 0; +} + GLFWAPI int glfwExtensionSupported(const char* extension) { _GLFWwindow* window; diff --git a/src/egl_context.c b/src/egl_context.c index 819a2b2d8..3342c73a5 100644 --- a/src/egl_context.c +++ b/src/egl_context.c @@ -233,11 +233,46 @@ static void swapBuffersEGL(_GLFWwindow* window) eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); } +static void swapBuffersWithDamageEGL(_GLFWwindow* window, GLFWrect* rects, int n_rects) +{ + if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: The context must be current on the calling thread when swapping buffers"); + return; + } + + if (eglSwapBuffersWithDamageKHR) + eglSwapBuffersWithDamageKHR(_glfw.egl.display, + window->context.egl.surface, + (EGLint*)rects, (EGLint)n_rects); + else + eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); +} + static void swapIntervalEGL(int interval) { eglSwapInterval(_glfw.egl.display, interval); } +static int getBufferAgeEGL(_GLFWwindow* window) +{ + EGLint buffer_age; + + if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: The context must be current on the calling thread when swapping buffers"); + return 0; + } + + if (!eglQuerySurface(_glfw.egl.display, window->context.egl.surface, + EGL_BUFFER_AGE_EXT, &buffer_age)) + return 0; + + return buffer_age; +} + static int extensionSupportedEGL(const char* extension) { const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); @@ -368,6 +403,8 @@ GLFWbool _glfwInitEGL(void) _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); _glfw.egl.QueryString = (PFN_eglQueryString) _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); + _glfw.egl.QuerySurface = (PFN_eglQuerySurface) + _glfw_dlsym(_glfw.egl.handle, "eglQuerySurface"); _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); @@ -386,6 +423,7 @@ GLFWbool _glfwInitEGL(void) !_glfw.egl.SwapBuffers || !_glfw.egl.SwapInterval || !_glfw.egl.QueryString || + !_glfw.egl.QuerySurface || !_glfw.egl.GetProcAddress) { _glfwInputError(GLFW_PLATFORM_ERROR, @@ -426,6 +464,12 @@ GLFWbool _glfwInitEGL(void) extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); _glfw.egl.KHR_context_flush_control = extensionSupportedEGL("EGL_KHR_context_flush_control"); + _glfw.egl.KHR_swap_buffers_with_damage = + extensionSupportedEGL("EGL_KHR_swap_buffers_with_damage"); + + if (_glfw.egl.KHR_swap_buffers_with_damage) + _glfw.egl.SwapBuffersWithDamageKHR = (PFN_eglSwapBuffersWithDamageKHR) + _glfw.egl.GetProcAddress("eglSwapBuffersWithDamageKHR"); return GLFW_TRUE; } @@ -693,7 +737,9 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, window->context.makeCurrent = makeContextCurrentEGL; window->context.swapBuffers = swapBuffersEGL; + window->context.swapBuffersWithDamage = swapBuffersWithDamageEGL; window->context.swapInterval = swapIntervalEGL; + window->context.getBufferAge = getBufferAgeEGL; window->context.extensionSupported = extensionSupportedEGL; window->context.getProcAddress = getProcAddressEGL; window->context.destroy = destroyContextEGL; diff --git a/src/egl_context.h b/src/egl_context.h index 8bfb7db6a..08176f76d 100644 --- a/src/egl_context.h +++ b/src/egl_context.h @@ -107,6 +107,8 @@ typedef struct wl_egl_window* EGLNativeWindowType; #define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 +#define EGL_BUFFER_AGE_EXT 0x313D + typedef int EGLint; typedef unsigned int EGLBoolean; typedef unsigned int EGLenum; @@ -131,7 +133,9 @@ typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLS typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglQuerySurface)(EGLDisplay,EGLSurface,EGLint,EGLint*); typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); +typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffersWithDamageKHR)(EGLDisplay,EGLSurface,EGLint*,EGLint); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs #define eglGetDisplay _glfw.egl.GetDisplay @@ -147,7 +151,9 @@ typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglSwapBuffers _glfw.egl.SwapBuffers #define eglSwapInterval _glfw.egl.SwapInterval #define eglQueryString _glfw.egl.QueryString +#define eglQuerySurface _glfw.egl.QuerySurface #define eglGetProcAddress _glfw.egl.GetProcAddress +#define eglSwapBuffersWithDamageKHR _glfw.egl.SwapBuffersWithDamageKHR #define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl #define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl @@ -178,6 +184,7 @@ typedef struct _GLFWlibraryEGL GLFWbool KHR_gl_colorspace; GLFWbool KHR_get_all_proc_addresses; GLFWbool KHR_context_flush_control; + GLFWbool KHR_swap_buffers_with_damage; void* handle; @@ -196,7 +203,9 @@ typedef struct _GLFWlibraryEGL PFN_eglSwapBuffers SwapBuffers; PFN_eglSwapInterval SwapInterval; PFN_eglQueryString QueryString; + PFN_eglQuerySurface QuerySurface; PFN_eglGetProcAddress GetProcAddress; + PFN_eglSwapBuffersWithDamageKHR SwapBuffersWithDamageKHR; } _GLFWlibraryEGL; diff --git a/src/internal.h b/src/internal.h index 6d7587c8c..2a9a90761 100644 --- a/src/internal.h +++ b/src/internal.h @@ -78,7 +78,9 @@ typedef struct _GLFWmutex _GLFWmutex; typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); +typedef void (* _GLFWswapbufferswithdamagefun)(_GLFWwindow*,GLFWrect*,int); typedef void (* _GLFWswapintervalfun)(int); +typedef int (* _GLFWgetbufferagefun)(_GLFWwindow*); typedef int (* _GLFWextensionsupportedfun)(const char*); typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); @@ -351,7 +353,9 @@ struct _GLFWcontext _GLFWmakecontextcurrentfun makeCurrent; _GLFWswapbuffersfun swapBuffers; + _GLFWswapbufferswithdamagefun swapBuffersWithDamage; _GLFWswapintervalfun swapInterval; + _GLFWgetbufferagefun getBufferAge; _GLFWextensionsupportedfun extensionSupported; _GLFWgetprocaddressfun getProcAddress; _GLFWdestroycontextfun destroy; diff --git a/src/wl_init.c b/src/wl_init.c index 558ff8a80..10948fdd4 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -788,7 +788,7 @@ static void registryHandleGlobal(void* data, { if (strcmp(interface, "wl_compositor") == 0) { - _glfw.wl.compositorVersion = min(3, version); + _glfw.wl.compositorVersion = min(4, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, _glfw.wl.compositorVersion); diff --git a/tests/msaa.c b/tests/msaa.c index 33e2ccc3b..842104477 100644 --- a/tests/msaa.c +++ b/tests/msaa.c @@ -102,6 +102,19 @@ int main(int argc, char** argv) GLuint vertex_buffer, vertex_shader, fragment_shader, program; GLint mvp_location, vpos_location; + // Minimum static damage for both squares. + GLFWrect rects[8] = + {{ 25, 25, 350, 90}, + { 25, 285, 350, 90}, + { 25, 115, 90, 170}, + {285, 115, 90, 170}, + + {425, 25, 350, 90}, + {425, 285, 350, 90}, + {425, 115, 90, 170}, + {685, 115, 90, 170}, + }; + while ((ch = getopt(argc, argv, "hs:")) != -1) { switch (ch) @@ -178,11 +191,13 @@ int main(int argc, char** argv) while (!glfwWindowShouldClose(window)) { float ratio; + int buffer_age; int width, height; mat4x4 m, p, mvp; const double angle = glfwGetTime() * M_PI / 180.0; glfwGetFramebufferSize(window, &width, &height); + buffer_age = glfwGetBufferAge(window); ratio = width / (float) height; glViewport(0, 0, width, height); @@ -208,7 +223,12 @@ int main(int argc, char** argv) glEnable(GL_MULTISAMPLE); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glfwSwapBuffers(window); + // If buffer_age is 0, we can’t assume anything about the previous buffer + // so swap the entire buffer. + if (buffer_age > 0) + glfwSwapBuffersWithDamage(window, rects, 8); + else + glfwSwapBuffers(window); glfwPollEvents(); }