diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-08 11:54:42 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2022-02-08 11:54:42 +0200 |
commit | c6b294a22b9d4e75854dd4e2d5c75f9b063e7bed (patch) | |
tree | e582d8f50d2041cae6be61acfa2e554afb944111 | |
parent | 1d978ce9fafd2658121a48288a6ddf16cfccecc6 (diff) |
macOS: Attempting workaround for Metal refresh issues
For some reason, rendering to a separate render target instead of directly to the window helps with certain refresh glitches.
More glitches still remain, though, perhaps related to not running window refresh at a constant rate but occasionally pausing to wait for input events.
-rw-r--r-- | src/app.c | 11 | ||||
-rw-r--r-- | src/ui/scrollwidget.c | 20 | ||||
-rw-r--r-- | src/ui/window.c | 38 | ||||
-rw-r--r-- | src/ui/window.h | 2 |
4 files changed, 57 insertions, 14 deletions
@@ -1644,13 +1644,14 @@ void refresh_App(void) { | |||
1644 | iWindow *win = j.ptr; | 1644 | iWindow *win = j.ptr; |
1645 | setCurrent_Window(win); | 1645 | setCurrent_Window(win); |
1646 | switch (win->type) { | 1646 | switch (win->type) { |
1647 | case main_WindowType: | 1647 | case main_WindowType: { |
1648 | // iTime draw; | 1648 | // iTime draw; |
1649 | // initCurrent_Time(&draw); | 1649 | // initCurrent_Time(&draw); |
1650 | draw_MainWindow(as_MainWindow(win)); | 1650 | draw_MainWindow(as_MainWindow(win)); |
1651 | // printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000)); | 1651 | // printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000)); |
1652 | // fflush(stdout); | 1652 | // fflush(stdout); |
1653 | break; | 1653 | break; |
1654 | } | ||
1654 | default: | 1655 | default: |
1655 | draw_Window(win); | 1656 | draw_Window(win); |
1656 | break; | 1657 | 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) { | |||
239 | init_Paint(&p); | 239 | init_Paint(&p); |
240 | /* Blend if opacity is not at maximum. */ | 240 | /* Blend if opacity is not at maximum. */ |
241 | p.alpha = 255 * value_Anim(&d->opacity); | 241 | p.alpha = 255 * value_Anim(&d->opacity); |
242 | SDL_Renderer *render = renderer_Window(get_Window()); | 242 | if (p.alpha > 0) { |
243 | if (p.alpha < 255) { | 243 | SDL_Renderer *render = renderer_Window(get_Window()); |
244 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND); | 244 | if (p.alpha < 255) { |
245 | } | 245 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND); |
246 | const iRect thumbRect = shrunk_Rect( | 246 | } |
247 | thumbRect_ScrollWidget_(d), init_I2(isPressed ? gap_UI : (gap_UI * 4 / 3), gap_UI / 2)); | 247 | const iRect thumbRect = shrunk_Rect( |
248 | fillRect_Paint(&p, thumbRect, isPressed ? uiBackgroundPressed_ColorId : tmQuote_ColorId); | 248 | thumbRect_ScrollWidget_(d), init_I2(isPressed ? gap_UI : (gap_UI * 4 / 3), gap_UI / 2)); |
249 | if (p.alpha < 255) { | 249 | fillRect_Paint(&p, thumbRect, isPressed ? uiBackgroundPressed_ColorId : tmQuote_ColorId); |
250 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); | 250 | if (p.alpha < 255) { |
251 | SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); | ||
252 | } | ||
251 | } | 253 | } |
252 | } | 254 | } |
253 | } | 255 | } |
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) { | |||
539 | void init_MainWindow(iMainWindow *d, iRect rect) { | 539 | void init_MainWindow(iMainWindow *d, iRect rect) { |
540 | theWindow_ = &d->base; | 540 | theWindow_ = &d->base; |
541 | theMainWindow_ = d; | 541 | theMainWindow_ = d; |
542 | d->enableBackBuf = iFalse; | ||
542 | uint32_t flags = 0; | 543 | uint32_t flags = 0; |
543 | #if defined (iPlatformAppleDesktop) | 544 | #if defined (iPlatformAppleDesktop) |
544 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); | 545 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl"); |
545 | flags |= shouldDefaultToMetalRenderer_MacOS() ? SDL_WINDOW_METAL : SDL_WINDOW_OPENGL; | 546 | flags |= shouldDefaultToMetalRenderer_MacOS() ? SDL_WINDOW_METAL : SDL_WINDOW_OPENGL; |
547 | if (flags & SDL_WINDOW_METAL) { | ||
548 | /* There are some really odd refresh glitches that only occur with the Metal | ||
549 | backend. It's perhaps related to it not expecting refresh to stop intermittently | ||
550 | to wait for input events. If forcing constant refreshing at full frame rate, the | ||
551 | problems seem to go away... Rendering everything to a separate render target | ||
552 | appears to sidestep some of the glitches. */ | ||
553 | d->enableBackBuf = iTrue; | ||
554 | } | ||
546 | #elif defined (iPlatformAppleMobile) | 555 | #elif defined (iPlatformAppleMobile) |
547 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); | 556 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); |
548 | flags |= SDL_WINDOW_METAL; | 557 | flags |= SDL_WINDOW_METAL; |
@@ -566,6 +575,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
566 | d->place.lastNotifiedSize = zero_I2(); | 575 | d->place.lastNotifiedSize = zero_I2(); |
567 | d->place.snap = 0; | 576 | d->place.snap = 0; |
568 | d->keyboardHeight = 0; | 577 | d->keyboardHeight = 0; |
578 | d->backBuf = NULL; | ||
569 | #if defined(iPlatformMobile) | 579 | #if defined(iPlatformMobile) |
570 | const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */ | 580 | const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */ |
571 | #else | 581 | #else |
@@ -637,6 +647,9 @@ void init_MainWindow(iMainWindow *d, iRect rect) { | |||
637 | } | 647 | } |
638 | 648 | ||
639 | void deinit_MainWindow(iMainWindow *d) { | 649 | void deinit_MainWindow(iMainWindow *d) { |
650 | if (d->backBuf) { | ||
651 | SDL_DestroyTexture(d->backBuf); | ||
652 | } | ||
640 | deinitRoots_Window_(as_Window(d)); | 653 | deinitRoots_Window_(as_Window(d)); |
641 | if (theWindow_ == as_Window(d)) { | 654 | if (theWindow_ == as_Window(d)) { |
642 | theWindow_ = NULL; | 655 | theWindow_ = NULL; |
@@ -682,6 +695,10 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) { | |||
682 | static void invalidate_MainWindow_(iMainWindow *d, iBool forced) { | 695 | static void invalidate_MainWindow_(iMainWindow *d, iBool forced) { |
683 | if (d && (!d->base.isInvalidated || forced)) { | 696 | if (d && (!d->base.isInvalidated || forced)) { |
684 | d->base.isInvalidated = iTrue; | 697 | d->base.isInvalidated = iTrue; |
698 | if (d->enableBackBuf && d->backBuf) { | ||
699 | SDL_DestroyTexture(d->backBuf); | ||
700 | d->backBuf = NULL; | ||
701 | } | ||
685 | resetFonts_Text(text_Window(d)); | 702 | resetFonts_Text(text_Window(d)); |
686 | postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ | 703 | postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ |
687 | } | 704 | } |
@@ -1272,11 +1289,28 @@ void draw_MainWindow(iMainWindow *d) { | |||
1272 | d->maxDrawableHeight = renderSize.y; | 1289 | d->maxDrawableHeight = renderSize.y; |
1273 | } | 1290 | } |
1274 | } | 1291 | } |
1292 | if (d->enableBackBuf) { | ||
1293 | /* Possible resize the backing buffer. */ | ||
1294 | if (!d->backBuf || !isEqual_I2(size_SDLTexture(d->backBuf), renderSize)) { | ||
1295 | if (d->backBuf) { | ||
1296 | SDL_DestroyTexture(d->backBuf); | ||
1297 | } | ||
1298 | d->backBuf = SDL_CreateTexture(d->base.render, | ||
1299 | SDL_PIXELFORMAT_RGB888, | ||
1300 | SDL_TEXTUREACCESS_TARGET, | ||
1301 | renderSize.x, | ||
1302 | renderSize.y); | ||
1303 | printf("NEW BACKING: %dx%d %p\n", renderSize.x, renderSize.y, d->backBuf); fflush(stdout); | ||
1304 | } | ||
1305 | } | ||
1275 | } | 1306 | } |
1276 | const int winFlags = SDL_GetWindowFlags(d->base.win); | 1307 | const int winFlags = SDL_GetWindowFlags(d->base.win); |
1277 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; | 1308 | const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; |
1278 | iPaint p; | 1309 | iPaint p; |
1279 | init_Paint(&p); | 1310 | init_Paint(&p); |
1311 | if (d->backBuf) { | ||
1312 | SDL_SetRenderTarget(d->base.render, d->backBuf); | ||
1313 | } | ||
1280 | /* Clear the window. The clear color is visible as a border around the window | 1314 | /* Clear the window. The clear color is visible as a border around the window |
1281 | when the custom frame is being used. */ { | 1315 | when the custom frame is being used. */ { |
1282 | setCurrent_Root(w->roots[0]); | 1316 | setCurrent_Root(w->roots[0]); |
@@ -1359,6 +1393,10 @@ void draw_MainWindow(iMainWindow *d) { | |||
1359 | drawCount_ = 0; | 1393 | drawCount_ = 0; |
1360 | #endif | 1394 | #endif |
1361 | } | 1395 | } |
1396 | if (d->backBuf) { | ||
1397 | SDL_SetRenderTarget(d->base.render, NULL); | ||
1398 | SDL_RenderCopy(d->base.render, d->backBuf, NULL, NULL); | ||
1399 | } | ||
1362 | #if 0 | 1400 | #if 0 |
1363 | /* Text cache debugging. */ { | 1401 | /* Text cache debugging. */ { |
1364 | SDL_Rect rect = { d->roots[0]->widget->rect.size.x - 640, 0, 640, 2.5 * 640 }; | 1402 | 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 { | |||
118 | SDL_Texture * appIcon; | 118 | SDL_Texture * appIcon; |
119 | int keyboardHeight; /* mobile software keyboards */ | 119 | int keyboardHeight; /* mobile software keyboards */ |
120 | int maxDrawableHeight; | 120 | int maxDrawableHeight; |
121 | iBool enableBackBuf; /* only used on macOS with Metal (helps with refresh glitches for some reason??) */ | ||
122 | SDL_Texture * backBuf; /* enables refreshing the window without redrawing anything */ | ||
121 | }; | 123 | }; |
122 | 124 | ||
123 | iLocalDef enum iWindowType type_Window(const iAnyWindow *d) { | 125 | iLocalDef enum iWindowType type_Window(const iAnyWindow *d) { |