Commit 9e0a8731 authored by Siarhei Siamashka's avatar Siarhei Siamashka
Browse files

DRI2: Workaround window resize bug in Mali r3p0 blob

The Mali blob is doing something like this:

 1. Request BackLeft DRI2 buffer (buffer A) and render to it
 2. Swap buffers
 3. Request BackLeft DRI2 buffer (buffer B)
 4. Check window size, and if it has changed - go back to step 1.
 5. Render to the current back buffer (either buffer A or B)
 6. Swap buffers
 7. Go back to step 4

A very serious show stopper problem is that the Mali blob ignores
DRI2-InvalidateBuffers events and just uses GetGeometry polling
to check whether the window size has changed. Unfortunately this
is racy and we may end up with a size mismatch between buffer A
and buffer B. This is particularly easy to trigger when the window
size changes exactly between steps 1 and 3.

See test/gles-yellow-blue-flip.c program which demonstrates this.
Qt5 applications also trigger this bug.

We workaround the issue by explicitly tracking the requests for
BackLeft buffers and checking whether the sizes of these buffers
match at step 1 and step 3. However the real challenge here is
notifying the client application that these buffers are no good,
so that it can request them again. As DRI2-InvalidateBuffers
events are ignored, we are in a pretty difficult situation.
Fortunately I remembered a weird behaviour observed earlier:

    https://groups.google.com/forum/#!msg/linux-sunxi/qnxpVaqp1Ys/aVTq09DVih0J



Actually if we return UMP secure ID value 1 for the second DRI2
buffer request, the blob responds to this by spitting out the
following error message:

    [EGL-X11] [2274] DETECTED ONLY ONE FRAMEBUFFER - FORCING A RESIZE
    [EGL-X11] [2274] DRI2 UMP ID 0x3 retrieved
    [EGL-X11] [2274] DRI2 WINDOW UMP SECURE ID CHANGED (0x3 -> 0x3)

And then it proceeds by re-trying to request a pair of DRI2 buffers.
But that's exactly the behaviour we want! As a down side, some ugly
flashing can be seen on screen at the time when this workaround kicks
in, but then everything normalizes. And unfortunately, the race
condition is still not totally eliminated because the blob is
apparently getting DRI2 buffer sizes from the separate GetGeometry
requests instead of using the information provided by DRI2GetBuffers.
But now the problem is at least very hard to trigger.
Signed-off-by: default avatarSiarhei Siamashka <siarhei.siamashka@gmail.com>
parent 0a3dbfba
......@@ -203,6 +203,7 @@ static DRI2Buffer2Ptr MaliDRI2CreateBuffer(DrawablePtr pDraw,
sunxi_disp_t *disp = SUNXI_DISP(pScrn);
Bool can_use_overlay = TRUE;
PixmapPtr pWindowPixmap;
DRI2WindowStatePtr window_state = NULL;
if (!(buffer = calloc(1, sizeof *buffer))) {
ErrorF("MaliDRI2CreateBuffer: calloc failed\n");
......@@ -267,7 +268,7 @@ static DRI2Buffer2Ptr MaliDRI2CreateBuffer(DrawablePtr pDraw,
/* The default common values */
buffer->attachment = attachment;
buffer->driverPrivate = privates;
buffer->format = format;
buffer->format = format + 1; /* hack to suppress DRI2 buffers reuse */
buffer->flags = 0;
buffer->cpp = pDraw->bitsPerPixel / 8;
/* Stride must be 8 bytes aligned for Mali400 */
......@@ -284,6 +285,38 @@ static DRI2Buffer2Ptr MaliDRI2CreateBuffer(DrawablePtr pDraw,
can_use_overlay = FALSE;
}
/* Allocate the DRI2-related window bookkeeping information */
if (attachment == DRI2BufferBackLeft && pDraw->type == DRAWABLE_WINDOW) {
HASH_FIND_PTR(private->HashWindowState, &pDraw, window_state);
if (!window_state) {
window_state = calloc(1, sizeof(*window_state));
window_state->pDraw = pDraw;
HASH_ADD_PTR(private->HashWindowState, pDraw, window_state);
DebugMsg("Allocate DRI2 bookkeeping for window %p\n", pDraw);
}
window_state->buf_request_cnt++;
}
/* For odd buffer requests save the window size */
if (window_state && (window_state->buf_request_cnt & 1)) {
/* remember window size for one buffer */
window_state->width = pDraw->width;
window_state->height = pDraw->height;
}
/* For even buffer requests check if the window size is still the same */
if (window_state && !(window_state->buf_request_cnt & 1) &&
(pDraw->width != window_state->width ||
pDraw->height != window_state->height)) {
DebugMsg("DRI2 buffers size mismatch detected, trying to recover\n");
privates->handle = UMP_INVALID_MEMORY_HANDLE;
privates->addr = NULL;
buffer->name = 1;
buffer->cpp = 0;
buffer->pitch = 0;
return buffer;
}
if (can_use_overlay) {
/* Use offscreen part of the framebuffer as an overlay */
privates->handle = UMP_INVALID_MEMORY_HANDLE;
......@@ -433,6 +466,9 @@ static void MaliDRI2CopyRegion(DrawablePtr pDraw,
MaliDRI2BufferPrivatePtr bufpriv = (MaliDRI2BufferPrivatePtr)pSrcBuffer->driverPrivate;
sunxi_disp_t *disp = SUNXI_DISP(xf86Screens[pScreen->myNum]);
if (!bufpriv->addr)
return;
UpdateOverlay(pScreen);
if (!drvpriv->bOverlayWinEnabled || bufpriv->handle != UMP_INVALID_MEMORY_HANDLE) {
......@@ -530,6 +566,14 @@ DestroyWindow(WindowPtr pWin)
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
SunxiMaliDRI2 *private = SUNXI_MALI_UMP_DRI2(pScrn);
Bool ret;
DrawablePtr pDraw = &pWin->drawable;
DRI2WindowStatePtr window_state = NULL;
HASH_FIND_PTR(private->HashWindowState, &pDraw, window_state);
if (window_state) {
DebugMsg("Free DRI2 bookkeeping for window %p\n", pWin);
HASH_DEL(private->HashWindowState, window_state);
free(window_state);
}
if (pWin == private->pOverlayWin) {
sunxi_disp_t *disp = SUNXI_DISP(pScrn);
......
......@@ -47,6 +47,38 @@ typedef struct
size_t height;
} UMPBufferInfoRec, *UMPBufferInfoPtr;
/*
* DRI2 related bookkeeping for windows. Because Mali r3p0 blob has
* quirks and needs workarounds, we can't fully rely on the Xorg DRI2
* framework. But instead have to predict what is happening on the
* client side based on the typical blob behavior.
*
* The blob is doing something like this:
* 1. Requests BackLeft DRI2 buffer (buffer A) and renders to it
* 2. Swaps buffers
* 3. Requests BackLeft DRI2 buffer (buffer B)
* 4. Checks window geometry, and if it has changed - go back to step 1.
* 5. Renders to the current back buffer (either buffer A or B)
* 6. Swaps buffers
* 7. Go back to step 4
*
* The main problem is that The Mali blob ignores DRI2-InvalidateBuffers
* events and just uses GetGeometry polling to check whether the window
* size has changed. Unfortunately this is racy and we may end up with a
* size mismatch between buffer A and buffer B. This is particularly easy
* to trigger when the window size changes exactly between steps 1 and 3.
* See test/gles-yellow-blue-flip.c program which demonstrates this.
*/
typedef struct
{
UT_hash_handle hh;
DrawablePtr pDraw;
/* width and height must be the same for back and front buffers */
int width, height;
/* the number of back buffer requests */
int buf_request_cnt;
} DRI2WindowStateRec, *DRI2WindowStatePtr;
typedef struct {
int overlay_x;
int overlay_y;
......@@ -68,6 +100,7 @@ typedef struct {
ump_secure_id ump_fb_secure_id;
UMPBufferInfoPtr HashPixmapToUMP;
DRI2WindowStatePtr HashWindowState;
int drm_fd;
} SunxiMaliDRI2;
......
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