Commit f5501ff1 authored by Siarhei Siamashka's avatar Siarhei Siamashka
Browse files

Backing store heuristics for improving windows dragging performance



This patch implements a heuristics, which enables backing store for some
windows. When backing store is enabled for a window, the window gets a
backing pixmap (via automatic redirection provided by composite extension).
It acts a bit similar to ShadowFB, but for individual windows.

The advantage of backing store is that we can avoid "expose event -> redraw"
animated trail in the exposed area when dragging another window on top of it.
Dragging windows becomes much smoother and faster.

But the disadvantage of backing store is the same as for ShadowFB. That's a
loss of precious RAM, extra buffer copy when somebody tries to update window
content, potentially skip of some frames on fast animation (they just do
not reach screen). Also hardware accelerated scrolling does not currently
work for the windows with backing store enabled.

We try to make the best use of backing store by enabling backing store for
all the windows that are direct children of root, except the one which has
keyboard focus (either directly or via one of its children). In practice this
heuristics seems to provide nearly perfect results:
 1) dragging windows is fast and smooth.
 2) the top level window with the keyboard focus (typically the application
    that a user is working with) is G2D accelerated and does not suffer from
    any intermediate buffer copy overhead.
Signed-off-by: default avatarSiarhei Siamashka <siarhei.siamashka@gmail.com>
parent 1bbeff2f
......@@ -63,6 +63,19 @@ Enable rotation of the display. The supported values are "CW" (clockwise,
90 degrees), "UD" (upside down, 180 degrees) and "CCW" (counter clockwise,
270 degrees). Implies use of the shadow framebuffer layer. Default: off.
.TP
.BI "Option \*qUseBackingStore\*q \*q" boolean \*q
Enable the use of backing store for certain windows at the bottom of the
stacking order. This allows to avoid expensive redraws caused by expose
events when dragging one window on top of another. The default heuristics
tries to be "smart" and avoid backing store allocation for the active
window which has keyboard focus. The purpose is to get the best balance
between performance and memory footprint without introducing full
compositing overhead. Default: inverse of ShadowFB
.TP
.BI "Option \*qForceBackingStore\*q \*q" boolean \*q
Same as "UseBackingStore" option, but don't apply any heuristics and just
allocate backing store for all windows.
.TP
.BI "Option \*qHWCursor\*q \*q" boolean \*q
Enable or disable the HW cursor. Default: on.
.TP
......
......@@ -36,6 +36,8 @@ sunxifb_drv_la_SOURCES = \
cpuinfo.h \
cpu_backend.c \
cpu_backend.h \
backing_store_tuner.c \
backing_store_tuner.h \
interfaces.h \
fbdev.c \
fbdev_priv.h \
......
/*
* Copyright © 2013 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "xorgVersion.h"
#include "xf86.h"
#include "fb.h"
#include "inputstr.h"
#include "fbdev_priv.h"
#include "backing_store_tuner.h"
/*
* This code implements a heuristics, which enables backing store for some
* windows. When backing store is enabled for a window, the window gets a
* backing pixmap (via automatic redirection provided by composite extension).
* It acts a bit similar to ShadowFB, but for individual windows.
*
* The advantage of backing store is that we can avoid "expose event -> redraw"
* animated trail in the exposed area when dragging another window on top of it.
* Dragging windows becomes much smoother and faster.
*
* But the disadvantage of backing store is the same as for ShadowFB. That's a
* loss of precious RAM, extra buffer copy when somebody tries to update window
* content, potentially skip of some frames on fast animation (they just do
* not reach screen). Also hardware accelerated scrolling does not currently
* work for the windows with backing store enabled.
*
* We try to make the best use of backing store by enabling backing store for
* all the windows that are direct children of root, except the one which has
* keyboard focus (either directly or via one of its children). In practice this
* heuristics seems to provide nearly perfect results:
* 1) dragging windows is fast and smooth.
* 2) the top level window with the keyboard focus (typically the application
* that a user is working with) is G2D accelerated and does not suffer from
* any intermediate buffer copy overhead.
*/
static void
xPostValidateTree(WindowPtr pWin, WindowPtr pLayerWin, VTKind kind)
{
ScreenPtr pScreen = pWin ? pWin->drawable.pScreen :
pLayerWin->drawable.pScreen;
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
BackingStoreTuner *private = BACKING_STORE_TUNER(pScrn);
WindowPtr curWin, focusWin = NULL;
/*
* Increment and backup the current counter. Because ChangeWindowAttributes
* may trigger nested PostValidateTree calls, we want to detect this
* situation and bail out (assuming that the nested PostValidateTree
* call already did the job)
*/
unsigned int CurrentCount = ++private->PostValidateTreeCount;
/* Call the original PostValidateTree */
if (private->PostValidateTree) {
pScreen->PostValidateTree = private->PostValidateTree;
(*pScreen->PostValidateTree) (pWin, pLayerWin, kind);
private->PostValidateTree = pScreen->PostValidateTree;
pScreen->PostValidateTree = xPostValidateTree;
}
/* Find the window with keyboard focus */
if (inputInfo.keyboard && inputInfo.keyboard->focus)
focusWin = inputInfo.keyboard->focus->win;
if (!pWin || !focusWin || focusWin == NoneWin || focusWin == PointerRootWin)
return;
/* Descend down to the window, which has the root window as a parent */
while (focusWin->parent && focusWin->parent != pScreen->root)
focusWin = focusWin->parent;
if (focusWin->parent != pScreen->root)
return;
/*
* We are a bit paranoid here and want to eliminate any possibility
* of infinite recursion
*/
if (private->PostValidateTreeNestingLevel > 4) {
DebugMsg("Oops, too much nesting for PostValidateTree, bailing out\n");
return;
}
private->PostValidateTreeNestingLevel++;
/* Disable backing store for the focus window */
if (!private->ForceBackingStore && focusWin->backStorage) {
DebugMsg("Disable backing store for the focus window 0x%x\n",
(unsigned int)focusWin->drawable.id);
pScreen->backingStoreSupport = Always;
focusWin->backingStore = NotUseful;
(*pScreen->ChangeWindowAttributes) (focusWin, CWBackingStore);
if (CurrentCount != private->PostValidateTreeCount) {
DebugMsg("Nested PostValidateTree in ChangeWindowAttributes\n");
private->PostValidateTreeNestingLevel--;
return;
}
}
/* And enable backing store for all the other children of root */
curWin = pScreen->root->firstChild;
while (curWin) {
if (!curWin->backStorage && (private->ForceBackingStore ||
curWin != focusWin)) {
DebugMsg("Enable backing store for window 0x%x\n",
(unsigned int)curWin->drawable.id);
pScreen->backingStoreSupport = Always;
curWin->backingStore = WhenMapped;
(*pScreen->ChangeWindowAttributes) (curWin, CWBackingStore);
if (CurrentCount != private->PostValidateTreeCount) {
DebugMsg("Nested PostValidateTree in ChangeWindowAttributes\n");
private->PostValidateTreeNestingLevel--;
return;
}
}
curWin = curWin->nextSib;
}
private->PostValidateTreeNestingLevel--;
}
static void
xReparentWindow(WindowPtr pWin, WindowPtr pPriorParent)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
BackingStoreTuner *private = BACKING_STORE_TUNER(pScrn);
if (private->ReparentWindow) {
pScreen->ReparentWindow = private->ReparentWindow;
(*pScreen->ReparentWindow) (pWin, pPriorParent);
private->ReparentWindow = pScreen->ReparentWindow;
pScreen->ReparentWindow = xReparentWindow;
}
/* We only want backing store set for direct children of root */
if (pPriorParent == pScreen->root && pWin->backStorage) {
DebugMsg("Reparent window 0x%x from root, disabling backing store\n",
(unsigned int)pWin->drawable.id);
pScreen->backingStoreSupport = Always;
pWin->backingStore = NotUseful;
(*pScreen->ChangeWindowAttributes) (pWin, CWBackingStore);
}
}
/*****************************************************************************/
BackingStoreTuner *BackingStoreTuner_Init(ScreenPtr pScreen, Bool force)
{
BackingStoreTuner *private = calloc(1, sizeof(BackingStoreTuner));
if (!private) {
xf86DrvMsg(pScreen->myNum, X_INFO,
"BackingStoreTuner_Init: calloc failed\n");
return NULL;
}
private->ForceBackingStore = force;
if (private->ForceBackingStore)
xf86DrvMsg(pScreen->myNum, X_INFO,
"automatically forcing backing store for all windows\n");
else
xf86DrvMsg(pScreen->myNum, X_INFO,
"using backing store heuristics\n");
/* Wrap the current PostValidateTree function */
private->PostValidateTree = pScreen->PostValidateTree;
pScreen->PostValidateTree = xPostValidateTree;
/* Wrap the current ReparentWindow function */
private->ReparentWindow = pScreen->ReparentWindow;
pScreen->ReparentWindow = xReparentWindow;
return private;
}
void BackingStoreTuner_Close(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
BackingStoreTuner *private = BACKING_STORE_TUNER(pScrn);
pScreen->PostValidateTree = private->PostValidateTree;
pScreen->ReparentWindow = private->ReparentWindow;
}
/*
* Copyright © 2013 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef BACKING_STORE_TUNER_H
#define BACKING_STORE_TUNER_H
#include "interfaces.h"
typedef struct {
/* Just enable backing store for all windows */
Bool ForceBackingStore;
unsigned int PostValidateTreeCount;
unsigned int PostValidateTreeNestingLevel;
PostValidateTreeProcPtr PostValidateTree;
ReparentWindowProcPtr ReparentWindow;
} BackingStoreTuner;
BackingStoreTuner *BackingStoreTuner_Init(ScreenPtr pScreen, Bool force);
void BackingStoreTuner_Close(ScreenPtr pScreen);
#endif
......@@ -51,6 +51,7 @@
#include "sunxi_disp.h"
#include "sunxi_disp_hwcursor.h"
#include "sunxi_x_g2d.h"
#include "backing_store_tuner.h"
#ifdef HAVE_LIBUMP
#include "sunxi_mali_ump_dri2.h"
......@@ -166,6 +167,8 @@ typedef enum {
OPTION_DRI2,
OPTION_DRI2_OVERLAY,
OPTION_ACCELMETHOD,
OPTION_USE_BS,
OPTION_FORCE_BS,
} FBDevOpts;
static const OptionInfoRec FBDevOptions[] = {
......@@ -178,6 +181,8 @@ static const OptionInfoRec FBDevOptions[] = {
{ OPTION_DRI2, "DRI2", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_DRI2_OVERLAY, "DRI2HWOverlay",OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_ACCELMETHOD, "AccelMethod", OPTV_STRING, {0}, FALSE },
{ OPTION_USE_BS, "UseBackingStore",OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_FORCE_BS, "ForceBackingStore",OPTV_BOOLEAN,{0}, FALSE },
{ -1, NULL, OPTV_NONE, {0}, FALSE }
};
......@@ -700,6 +705,7 @@ FBDevScreenInit(SCREEN_INIT_ARGS_DECL)
int type;
char *accelmethod;
cpu_backend_t *cpu_backend;
Bool useBackingStore = FALSE, forceBackingStore = FALSE;
TRACE_ENTER("FBDevScreenInit");
......@@ -866,6 +872,29 @@ FBDevScreenInit(SCREEN_INIT_ARGS_DECL)
xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
"Render extension initialisation failed\n");
/*
* by default make use of backing store (the driver decides for which
* windows it is beneficial) if shadow is not enabled.
*/
useBackingStore = xf86ReturnOptValBool(fPtr->Options, OPTION_USE_BS,
!fPtr->shadowFB);
#ifndef __arm__
/*
* right now we can only make "smart" decisions on ARM hardware,
* everything else (for example x86) would take a performance hit
* unless backing store is just used for all windows.
*/
forceBackingStore = useBackingStore;
#endif
/* but still honour the settings from xorg.conf */
forceBackingStore = xf86ReturnOptValBool(fPtr->Options, OPTION_FORCE_BS,
forceBackingStore);
if (useBackingStore || forceBackingStore) {
fPtr->backing_store_tuner_private =
BackingStoreTuner_Init(pScreen, forceBackingStore);
}
/* initialize the 'CPU' backend */
cpu_backend = cpu_backend_init(fPtr->fbmem, pScrn->videoRam);
fPtr->cpu_backend_private = cpu_backend;
......@@ -1080,6 +1109,12 @@ FBDevCloseScreen(CLOSE_SCREEN_ARGS_DECL)
fPtr->cpu_backend_private = NULL;
}
if (fPtr->backing_store_tuner_private) {
BackingStoreTuner_Close(fPtr->backing_store_tuner_private);
free(fPtr->backing_store_tuner_private);
fPtr->backing_store_tuner_private = NULL;
}
if (fPtr->pDGAMode) {
free(fPtr->pDGAMode);
fPtr->pDGAMode = NULL;
......
......@@ -55,6 +55,7 @@ typedef struct {
OptionInfoPtr Options;
void *cpu_backend_private;
void *backing_store_tuner_private;
void *sunxi_disp_private;;
void *SunxiDispHardwareCursor_private;
void *SunxiMaliDRI2_private;
......@@ -63,6 +64,9 @@ typedef struct {
#define FBDEVPTR(p) ((FBDevPtr)((p)->driverPrivate))
#define BACKING_STORE_TUNER(p) ((BackingStoreTuner *) \
(FBDEVPTR(p)->backing_store_tuner_private))
#define SUNXI_DISP(p) ((sunxi_disp_t *) \
(FBDEVPTR(p)->sunxi_disp_private))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment