Lagrange [release]

macOS: Attempting workaround for Metal refresh issues

=> c6b294a22b9d4e75854dd4e2d5c75f9b063e7bed

diff --git a/src/app.c b/src/app.c
index 891f5d6e..b6c48062 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1644,13 +1644,14 @@ void refresh_App(void) {
             iWindow *win = j.ptr;
             setCurrent_Window(win);
             switch (win->type) {
-                case main_WindowType:
-    //                iTime draw;
-    //                initCurrent_Time(&draw);
+                case main_WindowType: {
+//                    iTime draw;
+//                    initCurrent_Time(&draw);
                     draw_MainWindow(as_MainWindow(win));
-    //                printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000));
-    //                fflush(stdout);
+//                    printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000));
+//                    fflush(stdout);
                     break;
+                }
                 default:
                     draw_Window(win);
                     break;
diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c
index 651669c6..71e4873f 100644
--- a/src/ui/scrollwidget.c
+++ b/src/ui/scrollwidget.c
@@ -239,15 +239,17 @@ static void draw_ScrollWidget_(const iScrollWidget *d) {
         init_Paint(&p);
         /* Blend if opacity is not at maximum. */
         p.alpha = 255 * value_Anim(&d->opacity);
-        SDL_Renderer *render = renderer_Window(get_Window());
-        if (p.alpha < 255) {
-            SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND);
-        }
-        const iRect thumbRect = shrunk_Rect(
-            thumbRect_ScrollWidget_(d), init_I2(isPressed ? gap_UI : (gap_UI * 4 / 3), gap_UI / 2));
-        fillRect_Paint(&p, thumbRect, isPressed ? uiBackgroundPressed_ColorId : tmQuote_ColorId);
-        if (p.alpha < 255) {
-            SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);
+        if (p.alpha > 0) {
+            SDL_Renderer *render = renderer_Window(get_Window());
+            if (p.alpha < 255) {
+                SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND);
+            }
+            const iRect thumbRect = shrunk_Rect(
+                thumbRect_ScrollWidget_(d), init_I2(isPressed ? gap_UI : (gap_UI * 4 / 3), gap_UI / 2));
+            fillRect_Paint(&p, thumbRect, isPressed ? uiBackgroundPressed_ColorId : tmQuote_ColorId);
+            if (p.alpha < 255) {
+                SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);
+            }
         }
     }
 }
diff --git a/src/ui/window.c b/src/ui/window.c
index 13abc5fa..c80bfbe0 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -539,10 +539,19 @@ void deinit_Window(iWindow *d) {
 void init_MainWindow(iMainWindow *d, iRect rect) {
     theWindow_ = &d->base;
     theMainWindow_ = d;
+    d->enableBackBuf = iFalse;
     uint32_t flags = 0;
 #if defined (iPlatformAppleDesktop)
     SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl");
     flags |= shouldDefaultToMetalRenderer_MacOS() ? SDL_WINDOW_METAL : SDL_WINDOW_OPENGL;
+    if (flags & SDL_WINDOW_METAL) {
+        /* There are some really odd refresh glitches that only occur with the Metal 
+           backend. It's perhaps related to it not expecting refresh to stop intermittently
+           to wait for input events. If forcing constant refreshing at full frame rate, the
+           problems seem to go away... Rendering everything to a separate render target
+           appears to sidestep some of the glitches. */
+        d->enableBackBuf = iTrue;
+    }
 #elif defined (iPlatformAppleMobile)
     SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
     flags |= SDL_WINDOW_METAL;
@@ -566,6 +575,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) {
     d->place.lastNotifiedSize = zero_I2();
     d->place.snap             = 0;
     d->keyboardHeight         = 0;
+    d->backBuf                = NULL;
 #if defined(iPlatformMobile)
     const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */
 #else
@@ -637,6 +647,9 @@ void init_MainWindow(iMainWindow *d, iRect rect) {
 }
 
 void deinit_MainWindow(iMainWindow *d) {
+    if (d->backBuf) {
+        SDL_DestroyTexture(d->backBuf);
+    }
     deinitRoots_Window_(as_Window(d));
     if (theWindow_ == as_Window(d)) {
         theWindow_ = NULL;
@@ -682,6 +695,10 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) {
 static void invalidate_MainWindow_(iMainWindow *d, iBool forced) {
     if (d && (!d->base.isInvalidated || forced)) {
         d->base.isInvalidated = iTrue;
+        if (d->enableBackBuf && d->backBuf) {
+            SDL_DestroyTexture(d->backBuf);
+            d->backBuf = NULL;
+        }
         resetFonts_Text(text_Window(d));
         postCommand_App("theme.changed auto:1"); /* forces UI invalidation */
     }
@@ -1272,11 +1289,28 @@ void draw_MainWindow(iMainWindow *d) {
                 d->maxDrawableHeight = renderSize.y;
             }
         }
+        if (d->enableBackBuf) {
+            /* Possible resize the backing buffer. */
+            if (!d->backBuf || !isEqual_I2(size_SDLTexture(d->backBuf), renderSize)) {
+                if (d->backBuf) {
+                    SDL_DestroyTexture(d->backBuf);
+                }
+                d->backBuf = SDL_CreateTexture(d->base.render,
+                                               SDL_PIXELFORMAT_RGB888,
+                                               SDL_TEXTUREACCESS_TARGET,
+                                               renderSize.x,
+                                               renderSize.y);
+                printf("NEW BACKING: %dx%d %p\n", renderSize.x, renderSize.y, d->backBuf); fflush(stdout);
+            }
+        }
     }
     const int   winFlags = SDL_GetWindowFlags(d->base.win);
     const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0;
     iPaint p;
     init_Paint(&p);
+    if (d->backBuf) {
+        SDL_SetRenderTarget(d->base.render, d->backBuf);
+    }
     /* Clear the window. The clear color is visible as a border around the window
        when the custom frame is being used. */ {
         setCurrent_Root(w->roots[0]);
@@ -1359,6 +1393,10 @@ void draw_MainWindow(iMainWindow *d) {
         drawCount_ = 0;
 #endif
     }
+    if (d->backBuf) {
+        SDL_SetRenderTarget(d->base.render, NULL);
+        SDL_RenderCopy(d->base.render, d->backBuf, NULL, NULL);
+    }
 #if 0
     /* Text cache debugging. */ {
         SDL_Rect rect = { d->roots[0]->widget->rect.size.x - 640, 0, 640, 2.5 * 640 };
diff --git a/src/ui/window.h b/src/ui/window.h
index b4e348d2..5abf23eb 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -118,6 +118,8 @@ struct Impl_MainWindow {
     SDL_Texture * appIcon;
     int           keyboardHeight; /* mobile software keyboards */
     int           maxDrawableHeight;
+    iBool         enableBackBuf; /* only used on macOS with Metal (helps with refresh glitches for some reason??) */
+    SDL_Texture * backBuf; /* enables refreshing the window without redrawing anything */
 };
 
 iLocalDef enum iWindowType type_Window(const iAnyWindow *d) {
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/release/cdiff/c6b294a22b9d4e75854dd4e2d5c75f9b063e7bed
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
25.974529 milliseconds
Gemini-to-HTML Time
0.682607 milliseconds

This content has been proxied by September (ba2dc).