Compare commits

...

6 Commits

Author SHA1 Message Date
Emmanuel Gil Peyrot
8e51c4a577 Tests: Add damage tracking to MSAA
This lets compositors avoid re-rendering the entire buffer when only the
outside of the squares changed.

If glfwGetBufferAge() returns 0 for any reason (the buffer was just
created, there was an error, or the underlying API doesn’t track buffer
age), we swap the entire buffer again.
2019-12-08 18:40:19 +01:00
Emmanuel Gil Peyrot
c8233ce7d3 EGL: Implement glfwGetBufferAge()
This feature is provided by the EGL_EXT_buffer_age extension, otherwise
always return 0.
2019-12-08 18:40:19 +01:00
Emmanuel Gil Peyrot
0d2ce23071 Add a glfwGetBufferAge() symbol
This new API lets the application know when the current buffer was last
written to.  If it returns 0, it means the user shouldn’t rely on what
was last present in this buffer and should draw everything again, as
usual.

This provides a significant boost in efficiency, especially on tiling
GPUs, by letting the application avoid to clear and redraw everything at
every frame.
2019-12-08 18:40:19 +01:00
Emmanuel Gil Peyrot
a94b96c954 Wayland: Bump wl_compositor for damage_buffer
wl_surface supports a damage_buffer request since its version 4, which
requires wl_compositor to have been bound at that version too.

damage_buffer can then be used by the EGL implementation to optimise
eglSwapBuffersWithDamageKHR().
2019-12-08 18:40:19 +01:00
Emmanuel Gil Peyrot
8e354c2259 EGL: Implement glfwSwapBufferWithDamage()
This is provided by the EGL_KHR_swap_buffers_with_damage extension.
2019-12-08 18:40:19 +01:00
Emmanuel Gil Peyrot
e0b1f518cf Add a glfwSwapBuffersWithDamage() symbol
This new API helps the user’s compositor reduce memory bandwidth and
thus power usage by only re-rendering a bunch of dirty rectangles
instead of the entire application buffer.

There is no guarantee that it will effectively get used, it is perfectly
valid to continue damaging the entire buffer instead like with
glfwSwapBuffers() in some cases.
2019-12-08 18:40:19 +01:00
7 changed files with 222 additions and 2 deletions

View File

@ -1780,6 +1780,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.
@ -5584,6 +5611,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.
@ -5593,6 +5621,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 shouldnt 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
@ -5639,6 +5710,36 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window);
*/
GLFWAPI void glfwSwapInterval(int interval);
/*! @brief Returns the buffer age of the windows 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

View File

@ -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;

View File

@ -232,11 +232,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);
@ -367,6 +402,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");
@ -385,6 +422,7 @@ GLFWbool _glfwInitEGL(void)
!_glfw.egl.SwapBuffers ||
!_glfw.egl.SwapInterval ||
!_glfw.egl.QueryString ||
!_glfw.egl.QuerySurface ||
!_glfw.egl.GetProcAddress)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
@ -425,6 +463,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;
}
@ -694,7 +738,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;

View File

@ -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;

View File

@ -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*);
@ -350,7 +352,9 @@ struct _GLFWcontext
_GLFWmakecontextcurrentfun makeCurrent;
_GLFWswapbuffersfun swapBuffers;
_GLFWswapbufferswithdamagefun swapBuffersWithDamage;
_GLFWswapintervalfun swapInterval;
_GLFWgetbufferagefun getBufferAge;
_GLFWextensionsupportedfun extensionSupported;
_GLFWgetprocaddressfun getProcAddress;
_GLFWdestroycontextfun destroy;

View File

@ -783,7 +783,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);

View File

@ -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,6 +223,11 @@ int main(int argc, char** argv)
glEnable(GL_MULTISAMPLE);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
// If buffer_age is 0, we cant assume anything about the previous buffer
// so swap the entire buffer.
if (buffer_age > 0)
glfwSwapBuffersWithDamage(window, rects, 8);
else
glfwSwapBuffers(window);
glfwPollEvents();
}