From 3c3f9e36754c68a081f309e5479e81f33086ea91 Mon Sep 17 00:00:00 2001 From: elasota Date: Wed, 25 Nov 2020 15:09:31 -0500 Subject: [PATCH] Add 2-stage startup for mobile init so there's less black screen --- Aerofoil/GpSystemServices_Win32.cpp | 5 + Aerofoil/GpSystemServices_Win32.h | 1 + .../app/jni/main/GpSystemServices_Android.cpp | 5 + .../app/jni/main/GpSystemServices_Android.h | 1 + AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp | 2 +- ApplicationResourcePatches/PICT/1303.bmp | Bin 0 -> 73782 bytes ApplicationResourcePatches/manifest.json | 1 + GpApp/Main.cpp | 416 ++++++++++++++---- GpCommon/IGpSystemServices.h | 1 + PortabilityLayer/QDStandardPalette.cpp | 27 +- PortabilityLayer/QDStandardPalette.h | 6 +- 11 files changed, 377 insertions(+), 88 deletions(-) create mode 100644 ApplicationResourcePatches/PICT/1303.bmp diff --git a/Aerofoil/GpSystemServices_Win32.cpp b/Aerofoil/GpSystemServices_Win32.cpp index ad1302d..1da5a3d 100644 --- a/Aerofoil/GpSystemServices_Win32.cpp +++ b/Aerofoil/GpSystemServices_Win32.cpp @@ -144,6 +144,11 @@ bool GpSystemServices_Win32::IsTextInputObstructive() const return false; } +bool GpSystemServices_Win32::IsFullscreenPreferred() const +{ + return !m_isTouchscreenSimulation; +} + unsigned int GpSystemServices_Win32::GetCPUCount() const { SYSTEM_INFO sysInfo; diff --git a/Aerofoil/GpSystemServices_Win32.h b/Aerofoil/GpSystemServices_Win32.h index 795fc87..e48c263 100644 --- a/Aerofoil/GpSystemServices_Win32.h +++ b/Aerofoil/GpSystemServices_Win32.h @@ -31,6 +31,7 @@ public: bool IsTouchscreen() const override; bool IsUsingMouseAsTouch() const override; bool IsTextInputObstructive() const override; + bool IsFullscreenPreferred() const override; unsigned int GetCPUCount() const override; void SetTextInputEnabled(bool isEnabled) override; bool IsTextInputEnabled() const override; diff --git a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp index 4e39693..f7083ee 100644 --- a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp +++ b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.cpp @@ -265,6 +265,11 @@ bool GpSystemServices_Android::IsTextInputObstructive() const return true; } +bool GpSystemServices_Android::IsFullscreenPreferred() const +{ + return true; +} + unsigned int GpSystemServices_Android::GetCPUCount() const { return SDL_GetCPUCount(); diff --git a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h index 3e5c535..52bc820 100644 --- a/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h +++ b/AerofoilAndroid/app/jni/main/GpSystemServices_Android.h @@ -19,6 +19,7 @@ public: bool IsTouchscreen() const override; bool IsUsingMouseAsTouch() const override; bool IsTextInputObstructive() const override; + bool IsFullscreenPreferred() const override; unsigned int GetCPUCount() const override; void SetTextInputEnabled(bool isEnabled) override; bool IsTextInputEnabled() const override; diff --git a/AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp b/AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp index c853f00..ee194b1 100644 --- a/AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp +++ b/AerofoilSDL/GpDisplayDriver_SDL_GL2.cpp @@ -1215,7 +1215,7 @@ GpDisplayDriver_SDL_GL2::GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties m_bgColor[3] = 1.f; // Stupid hack to detect mobile... - m_isFullScreenDesired = m_properties.m_systemServices->IsTouchscreen() && !m_properties.m_systemServices->IsUsingMouseAsTouch(); + m_isFullScreenDesired = m_properties.m_systemServices->IsFullscreenPreferred(); const intmax_t periodNum = std::chrono::high_resolution_clock::period::num; const intmax_t periodDen = std::chrono::high_resolution_clock::period::den; diff --git a/ApplicationResourcePatches/PICT/1303.bmp b/ApplicationResourcePatches/PICT/1303.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e7d71e6e52617d8a64cec721e9cc47a0c684c43a GIT binary patch literal 73782 zcmd7b2fS3()i-`iOccAQQ9(e83Id7^u~QWU8%>%@xnKdLD+;2B9SaE901H^67`usp zg(3>JC~CwGVvRnD3TmQ0pZ|ByCMPE|_s*S}>-+xSBe^*cinj7jjL6w_PgKx z&dIOR%`LXr0-YVJSKn^C?W$L=?naJZg)$93Lb9Yf(n0Ns}f$d-NDIaL~kw6EB)D zVchuf=bwLm@7}#Tb?VfhL4zvZNVgj`Y}md-hcnJNqpYm#)KgFG(W7Ut-o20S)TzZG zhwQc2UR!UywWlljE~c`1^X5k!aYUz1$9L=2t!vk=-Me=?;l$%Rbm(x~LaKeS-FTCpNtFE}>ic2rO^pZ<1x#*&c#$P!8 zg7Ysp_uO+wjv6(5_=vv!`W=4QVY*S&BY5hDd+&`&u;RrRUwq+(7hZ6|1!K<}J9^BR z5hF*QbIv(Kh71`rXb>*#+~wqh4mt>E6h&WST-Ua3TXfDm^GsbGHf;DgL(e(;?6c1} z48wtdHrolZLWGsG|`-_~3&v7-lF6OHsaL;>0P}Pr2^e>#m+W`N}J=yzH{e0{>im(fINB z=lpZeJ9pIRz&}HW4mBx#`t-II3Lm2V_S+BIAT{aINnmZlgbT-w8#i|Bzxij_upxto zoORY&W@W&D0VbuxamO{>yJ6u|?5W3$9t|q7`g!M_7rHuP1hyPJcyIu^fo1*s^*`;j z)38vF9z9P!`DBZ_!Jc~-J_{{cv@oS3MvNSK&N)Me3_bg-vj?4VhOYKY@lThoT~RhE z5J~&Gb?X*BKKnOoW=i|^?K_~Xtlz)8+OtQ`Zr!_g?$YH%bKU89W76)JV_LUq-Lhp% zh(YcbNFS^#w{1nrp7P>Z&U*zx?t^mrTMx7iQ$wm@)8Y2vbQok>#SEdMO;w%cwepLDW*hOSaG^`vBw_Uuwf(g^FM-LEq|+K%(-JC0uM0)gU{|ipugwp(W3_`iD$ZW?$Wty zS1mJ(`ndM(+gr}WM5V;RLW@HW#W_Y5az?1c>L^E2kU>|?FZ?;?n09Siw{CfK%P`T0 zwy0OPUZvt}n{Bo^;DG&m_Uz#npfao=u%@dgorEktJmT=f1OK#Yb@Wk39e%{&haB1h z{zF=WU$bV7N|mlN&OGDRTW`JjrkiiPamEePrcJqi>Vydss5&lBKRe-j)KN!U8iR)n z_Lv!<2Z`8CmXkAVdBx?I4>^0VaiB$57d!8~bIqE&hrhem)cH=G zI`%oW +n94Vp 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 - +int loadScreenWindowPhase; int loadScreenRingStep; WindowPtr loadScreenWindow; Rect loadScreenProgressBarRect; @@ -249,7 +250,7 @@ void ReadInPrefs (void) doComplainDialogs = true; IGpDisplayDriver *displayDriver = PLDrivers::GetDisplayDriver(); - if (!displayDriver->IsFullScreen()) + if (PLDrivers::GetSystemServices()->IsFullscreenPreferred() != displayDriver->IsFullScreen()) displayDriver->RequestToggleFullScreen(0); modulePrefs.Dispose(); @@ -371,18 +372,171 @@ void StepLoadScreenRing() Rect ringDestRect = Rect::Create(8, 8, 24, 24); Rect ringSrcRect = Rect::Create(0, 0, 16, 16) + Point::Create((loadScreenRingStep / loadScreenStepGranularity) * 16, 0); + if (loadScreenWindowPhase == 0) + { + ringDestRect = Rect::Create(0, 0, 64, 64); + + const int progression = (loadScreenRingStep / loadScreenStepGranularity); + ringSrcRect = Rect::Create(0, 0, 64, 64) + Point::Create((progression % 6) * 64, (progression / 6) * 64); + } + CopyBits(*loadScreenRingSurface->m_port.GetPixMap(), *loadScreenWindow->GetDrawSurface()->m_port.GetPixMap(), &ringSrcRect, &ringDestRect, srcCopy); loadScreenWindow->GetDrawSurface()->m_port.SetDirty(PortabilityLayer::QDPortDirtyFlag_Contents); } } + +void CreateLoadScreenWindow(int phase) +{ + if (loadScreenRingSurface) + { + DisposeGWorld(loadScreenRingSurface); + loadScreenRingSurface = nullptr; + } + + if (loadScreenWindow) + { + PortabilityLayer::WindowManager::GetInstance()->DestroyWindow(loadScreenWindow); + loadScreenWindow = nullptr; + } + + if (phase == 0) + { + static const int kLoadScreenHeight = 64; + static const int kLoadScreenWidth = 64; + static const int kLoadRingResource = 1303; + + ForceSyncFrame(); + PLSysCalls::Sleep(1); + + THandle loadRingImageH = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('PICT', kLoadRingResource).StaticCast(); + BitmapImage *loadRingImage = *loadRingImageH; + + DrawSurface *loadRingSurface = nullptr; + Rect loadRingRect = loadRingImage->GetRect(); + loadRingRect.right *= 2; + loadRingRect.bottom *= 2; + CreateOffScreenGWorld(&loadRingSurface, &loadRingRect); + loadRingSurface->DrawPicture(loadRingImageH, loadRingRect); + + int32_t lsX = (thisMac.fullScreen.Width() - kLoadScreenWidth) / 2; + int32_t lsY = (thisMac.fullScreen.Height() - kLoadScreenHeight) / 2; + + + const Rect loadScreenRect = Rect::Create(lsY, lsX, lsY + kLoadScreenHeight, lsX + kLoadScreenWidth); + const Rect loadScreenLocalRect = Rect::Create(0, 0, loadScreenRect.Height(), loadScreenRect.Width()); + + PortabilityLayer::WindowDef def = PortabilityLayer::WindowDef::Create(loadScreenRect, PortabilityLayer::WindowStyleFlags::kBorderless, true, 0, 0, PSTR("")); + + loadScreenWindow = PortabilityLayer::WindowManager::GetInstance()->CreateWindow(def); + PortabilityLayer::WindowManager::GetInstance()->PutWindowBehind(loadScreenWindow, PL_GetPutInFrontWindowPtr()); + + DrawSurface *surface = loadScreenWindow->GetDrawSurface(); + PortabilityLayer::ResolveCachingColor blackColor(StdColors::Black()); + + surface->FillRect(loadScreenLocalRect, blackColor); + + loadScreenProgressBarRect = Rect::Create(0, 0, 0, 0); + loadScreenProgress = 0; + + Rect ringDestRect = Rect::Create(0, 0, 64, 64); + Rect ringSrcRect = Rect::Create(0, 0, 64, 64); + CopyBits(*loadRingSurface->m_port.GetPixMap(), *surface->m_port.GetPixMap(), &ringSrcRect, &ringDestRect, srcCopy); + + loadRingImageH.Dispose(); + + loadScreenRingSurface = loadRingSurface; + } + else if (phase == 1) + { + static const int kLoadScreenHeight = 32; + static const int kLoadRingResource = 1302; + + int kLoadScreenWidth = 296; + PLPasStr loadingText = PSTR("Loading..."); + + if (!isPrefsLoaded) + { + loadingText = PSTR("Getting some things ready..."); + kLoadScreenWidth = 440; + } + + ForceSyncFrame(); + PLSysCalls::Sleep(1); + + THandle loadRingImageH = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('PICT', kLoadRingResource).StaticCast(); + BitmapImage *loadRingImage = *loadRingImageH; + + DrawSurface *loadRingSurface = nullptr; + Rect loadRingRect = loadRingImage->GetRect(); + CreateOffScreenGWorld(&loadRingSurface, &loadRingRect); + loadRingSurface->DrawPicture(loadRingImageH, loadRingRect); + + int32_t lsX = (thisMac.fullScreen.Width() - kLoadScreenWidth) / 2; + int32_t lsY = (thisMac.fullScreen.Height() - kLoadScreenHeight) / 2; + + + const Rect loadScreenRect = Rect::Create(lsY, lsX, lsY + kLoadScreenHeight, lsX + kLoadScreenWidth); + const Rect loadScreenLocalRect = Rect::Create(0, 0, loadScreenRect.Height(), loadScreenRect.Width()); + + PortabilityLayer::WindowDef def = PortabilityLayer::WindowDef::Create(loadScreenRect, PortabilityLayer::WindowStyleFlags::kAlert, true, 0, 0, PSTR("")); + + loadScreenWindow = PortabilityLayer::WindowManager::GetInstance()->CreateWindow(def); + PortabilityLayer::WindowManager::GetInstance()->PutWindowBehind(loadScreenWindow, PL_GetPutInFrontWindowPtr()); + + DrawSurface *surface = loadScreenWindow->GetDrawSurface(); + PortabilityLayer::ResolveCachingColor blackColor(StdColors::Black()); + PortabilityLayer::ResolveCachingColor whiteColor(StdColors::White()); + + surface->FillRect(loadScreenLocalRect, whiteColor); + + PortabilityLayer::WindowManager::GetInstance()->FlickerWindowIn(loadScreenWindow, 32); + + PortabilityLayer::RenderedFont *font = GetApplicationFont(18, PortabilityLayer::FontFamilyFlag_None, true); + int32_t textY = (kLoadScreenHeight + font->GetMetrics().m_ascent) / 2; + surface->DrawString(Point::Create(4 + 16 + 8, textY), loadingText, blackColor, font); + + static const int32_t loadBarPadding = 16; + static const int32_t loadBarHeight = 10; + int32_t loadBarStartX = static_cast(font->MeasureString(loadingText.UChars(), loadingText.Length())) + 4 + 16 + 8 + loadBarPadding; + int32_t loadBarEndX = loadScreenLocalRect.right - loadBarPadding; + + loadScreenProgressBarRect = Rect::Create((loadScreenLocalRect.Height() - loadBarHeight) / 2, loadBarStartX, (loadScreenLocalRect.Height() + loadBarHeight) / 2, loadBarEndX); + + PortabilityLayer::ResolveCachingColor partialFillColor(PortabilityLayer::RGBAColor::Create(255, 255, 204, 255)); + surface->FrameRect(loadScreenProgressBarRect, blackColor); + surface->FillRect(loadScreenProgressBarRect.Inset(1, 1), partialFillColor); + + Rect ringDestRect = Rect::Create(8, 8, 24, 24); + Rect ringSrcRect = Rect::Create(0, 0, 16, 16); + CopyBits(*loadRingSurface->m_port.GetPixMap(), *surface->m_port.GetPixMap(), &ringSrcRect, &ringDestRect, srcCopy); + + loadRingImageH.Dispose(); + + loadScreenRingSurface = loadRingSurface; + } +} + void StepLoadScreen(int steps) { if (loadScreenWindow) { - Rect loadScreenProgressBarFillRect = loadScreenProgressBarRect.Inset(1, 1); + static const int kLoadScreenPhaseSwitchBreakpoint = 20; + int oldProgress = loadScreenProgress; - int loadScreenMax = 32; + + if (loadScreenProgress + steps >= kLoadScreenPhaseSwitchBreakpoint && loadScreenWindowPhase == 0) + { + loadScreenWindowPhase = 1; + CreateLoadScreenWindow(loadScreenWindowPhase); + + // Reset old progress since the progress bar was redrawn + oldProgress = 0; + } + + const Rect loadScreenProgressBarFillRect = loadScreenProgressBarRect.Inset(1, 1); + + int loadScreenMax = 42; loadScreenProgress = loadScreenProgress + steps; if (loadScreenProgress > loadScreenMax) loadScreenProgress = loadScreenMax; @@ -411,72 +565,12 @@ void InitLoadingWindow() if (!thisMac.isTouchscreen) return; - static const int kLoadScreenHeight = 32; - static const int kLoadRingResource = 1302; + if (isPrefsLoaded) + loadScreenWindowPhase = 1; + else + loadScreenWindowPhase = 0; - int kLoadScreenWidth = 296; - PLPasStr loadingText = PSTR("Loading..."); - - if (!isPrefsLoaded) - { - loadingText = PSTR("Getting some things ready..."); - kLoadScreenWidth = 440; - } - - ForceSyncFrame(); - PLSysCalls::Sleep(1); - - THandle loadRingImageH = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('PICT', kLoadRingResource).StaticCast(); - BitmapImage *loadRingImage = *loadRingImageH; - - DrawSurface *loadRingSurface = nullptr; - Rect loadRingRect = loadRingImage->GetRect(); - CreateOffScreenGWorld(&loadRingSurface, &loadRingRect); - loadRingSurface->DrawPicture(loadRingImageH, loadRingRect); - - int32_t lsX = (thisMac.fullScreen.Width() - kLoadScreenWidth) / 2; - int32_t lsY = (thisMac.fullScreen.Height() - kLoadScreenHeight) / 2; - - - const Rect loadScreenRect = Rect::Create(lsY, lsX, lsY + kLoadScreenHeight, lsX + kLoadScreenWidth); - const Rect loadScreenLocalRect = Rect::Create(0, 0, loadScreenRect.Height(), loadScreenRect.Width()); - - PortabilityLayer::WindowDef def = PortabilityLayer::WindowDef::Create(loadScreenRect, PortabilityLayer::WindowStyleFlags::kAlert, true, 0, 0, PSTR("")); - - loadScreenWindow = PortabilityLayer::WindowManager::GetInstance()->CreateWindow(def); - PortabilityLayer::WindowManager::GetInstance()->PutWindowBehind(loadScreenWindow, PL_GetPutInFrontWindowPtr()); - - DrawSurface *surface = loadScreenWindow->GetDrawSurface(); - PortabilityLayer::ResolveCachingColor blackColor(StdColors::Black()); - PortabilityLayer::ResolveCachingColor whiteColor(StdColors::White()); - - surface->FillRect(loadScreenLocalRect, whiteColor); - - PortabilityLayer::WindowManager::GetInstance()->FlickerWindowIn(loadScreenWindow, 32); - - PortabilityLayer::RenderedFont *font = GetApplicationFont(18, PortabilityLayer::FontFamilyFlag_None, true); - int32_t textY = (kLoadScreenHeight + font->GetMetrics().m_ascent) / 2; - surface->DrawString(Point::Create(4+16+8, textY), loadingText, blackColor, font); - - static const int32_t loadBarPadding = 16; - static const int32_t loadBarHeight = 10; - int32_t loadBarStartX = static_cast(font->MeasureString(loadingText.UChars(), loadingText.Length())) + 4 + 16 + 8 + loadBarPadding; - int32_t loadBarEndX = loadScreenLocalRect.right - loadBarPadding; - - loadScreenProgressBarRect = Rect::Create((loadScreenLocalRect.Height() - loadBarHeight) / 2, loadBarStartX, (loadScreenLocalRect.Height() + loadBarHeight) / 2, loadBarEndX); - loadScreenProgress = 0; - - PortabilityLayer::ResolveCachingColor partialFillColor(PortabilityLayer::RGBAColor::Create(255, 255, 204, 255)); - surface->FrameRect(loadScreenProgressBarRect, blackColor); - surface->FillRect(loadScreenProgressBarRect.Inset(1, 1), partialFillColor); - - Rect ringDestRect = Rect::Create(8, 8, 24, 24); - Rect ringSrcRect = Rect::Create(0, 0, 16, 16); - CopyBits(*loadRingSurface->m_port.GetPixMap(), *surface->m_port.GetPixMap(), &ringSrcRect, &ringDestRect, srcCopy); - - loadRingImageH.Dispose(); - - loadScreenRingSurface = loadRingSurface; + CreateLoadScreenWindow(loadScreenWindowPhase); } enum PreloadFontCategory @@ -542,12 +636,12 @@ void PreloadSingleFont (const PreloadFontSpec &spec) } } -void PreloadThreadFunc(void *context) +void PreloadFontThreadFunc(void *context) { PreloadFontWorkSlot *wSlot = static_cast(context); PreloadSingleFont(*wSlot->m_spec); - ++wSlot->m_singleJobCompleted; + wSlot->m_singleJobCompleted.fetch_add(1, std::memory_order_release); wSlot->m_completedEvent->Signal(); } @@ -555,6 +649,9 @@ void PreloadFonts() { static PreloadFontSpec specs[] = { + // First entry should be the one needed to show the load screen + { FontCategory_Application, 18, PortabilityLayer::FontFamilyFlag_None, true }, + { FontCategory_System, 9, PortabilityLayer::FontFamilyFlag_Bold, true }, { FontCategory_System, 10, PortabilityLayer::FontFamilyFlag_Bold, true }, { FontCategory_System, 12, PortabilityLayer::FontFamilyFlag_None, true }, @@ -563,7 +660,6 @@ void PreloadFonts() { FontCategory_Application, 9, PortabilityLayer::FontFamilyFlag_None, true }, { FontCategory_Application, 12, PortabilityLayer::FontFamilyFlag_Bold, true }, { FontCategory_Application, 14, PortabilityLayer::FontFamilyFlag_Bold, true }, - { FontCategory_Application, 18, PortabilityLayer::FontFamilyFlag_None, true }, { FontCategory_Application, 40, PortabilityLayer::FontFamilyFlag_None, true }, { FontCategory_Handwriting, 24, PortabilityLayer::FontFamilyFlag_None, true }, { FontCategory_Handwriting, 48, PortabilityLayer::FontFamilyFlag_None, true }, @@ -588,7 +684,7 @@ void PreloadFonts() { if (slot.m_queued) { - if (slot.m_singleJobCompleted.load(std::memory_order_relaxed) != 0) + if (slot.m_singleJobCompleted.load(std::memory_order_acquire) != 0) { slot.m_completedEvent->Wait(); slot.m_queued = false; @@ -603,9 +699,9 @@ void PreloadFonts() if (queuedSpecs < numFontSpecs) { slot.m_queued = true; - slot.m_singleJobCompleted.store(0); slot.m_spec = specs + queuedSpecs; - slot.m_workerThread->AsyncExecuteTask(PreloadThreadFunc, &slot); + slot.m_singleJobCompleted.store(0, std::memory_order_release); + slot.m_workerThread->AsyncExecuteTask(PreloadFontThreadFunc, &slot); queuedSpecs++; } @@ -616,6 +712,55 @@ void PreloadFonts() } } +struct PreloadAATableSpec +{ + PortabilityLayer::RGBAColor m_color; + bool m_isTone; +}; + +struct PreloadAATableWorkSlot +{ + IGpThreadEvent *m_completedEvent; + IGpMutex *m_mutex; + PortabilityLayer::WorkerThread *m_workerThread; + std::atomic m_singleJobCompleted; + const PreloadAATableSpec *m_spec; + bool m_queued; + + PreloadAATableWorkSlot(); + ~PreloadAATableWorkSlot(); +}; + +PreloadAATableWorkSlot::PreloadAATableWorkSlot() + : m_completedEvent(nullptr) + , m_workerThread(nullptr) + , m_spec(nullptr) + , m_queued(false) +{ +} + +PreloadAATableWorkSlot::~PreloadAATableWorkSlot() +{ + if (m_workerThread) + m_workerThread->Destroy(); +} + + +void PreloadAATableThreadFunc(void *context) +{ + PreloadAATableWorkSlot *wSlot = static_cast(context); + + PortabilityLayer::StandardPalette *sp = PortabilityLayer::StandardPalette::GetInstance(); + + if (wSlot->m_spec->m_isTone) + sp->GetCachedToneAATable(wSlot->m_spec->m_color.r, wSlot->m_mutex); + else + sp->GetCachedPaletteAATable(wSlot->m_spec->m_color, wSlot->m_mutex); + + wSlot->m_singleJobCompleted.fetch_add(1, std::memory_order_release); + wSlot->m_completedEvent->Signal(); +} + void PreloadAATables() { PortabilityLayer::StandardPalette *sp = PortabilityLayer::StandardPalette::GetInstance(); @@ -630,15 +775,119 @@ void PreloadAATables() PortabilityLayer::RGBAColor::Create(204, 102, 51, 255), }; - const size_t numPreloads = sizeof(preloadColors) / sizeof(preloadColors[0]); + const size_t numPalettePreloads = sizeof(preloadColors) / sizeof(preloadColors[0]); + const size_t maxTonePreloads = numPalettePreloads * 3; - for (size_t i = 0; i < numPreloads; i++) + PreloadAATableSpec specs[numPalettePreloads + maxTonePreloads]; + for (size_t i = 0; i < numPalettePreloads; i++) { - sp->GetCachedPaletteAATable(preloadColors[i]); - sp->GetCachedToneAATable(preloadColors[i].r); - sp->GetCachedToneAATable(preloadColors[i].g); - sp->GetCachedToneAATable(preloadColors[i].b); - StepLoadScreen(1); + specs[i].m_color = preloadColors[i]; + specs[i].m_isTone = false; + } + + size_t numTonePreloads = 0; + for (size_t i = 0; i < numPalettePreloads; i++) + { + const uint8_t rgb[3] = { preloadColors[i].r, preloadColors[i].g, preloadColors[i].b }; + + for (int ch = 0; ch < 3; ch++) + { + uint8_t tone = rgb[ch]; + + bool toneAlreadyQueued = false; + for (size_t j = 0; j < numTonePreloads; j++) + { + if (specs[numPalettePreloads + j].m_color.r == tone) + { + toneAlreadyQueued = true; + break; + } + } + + if (!toneAlreadyQueued) + { + PreloadAATableSpec &spec = specs[i + numTonePreloads]; + numTonePreloads++; + + spec.m_color = PortabilityLayer::RGBAColor::Create(tone, tone, tone, 255); + spec.m_isTone = true; + } + } + } + + PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); + + const int numAASpecs = numPalettePreloads + numTonePreloads; + + unsigned int cpus = PLDrivers::GetSystemServices()->GetCPUCount(); + if (cpus < 2) + { + for (size_t i = 0; i < numAASpecs; i++) + { + PreloadAATableThreadFunc(specs + i); + StepLoadScreen(1); + } + } + else + { + cpus -= 1; + + int queuedSpecs = 0; + int completedSpecs = 0; + + PreloadAATableWorkSlot *slots = static_cast(mm->Alloc(sizeof(PreloadAATableWorkSlot) * cpus)); + + IGpMutex *mutex = PLDrivers::GetSystemServices()->CreateMutex(); + + for (unsigned int i = 0; i < cpus; i++) + { + PreloadAATableWorkSlot *slot = new (slots + i) PreloadAATableWorkSlot(); + slot->m_workerThread = PortabilityLayer::WorkerThread::Create(); + slot->m_completedEvent = PLDrivers::GetSystemServices()->CreateThreadEvent(true, false); + slot->m_mutex = mutex; + } + + while (completedSpecs < numAASpecs) + { + for (unsigned int i = 0; i < cpus; i++) + { + PreloadAATableWorkSlot &slot = slots[i]; + + if (slot.m_queued) + { + if (slot.m_singleJobCompleted.load(std::memory_order_acquire) != 0) + { + slot.m_completedEvent->Wait(); + slot.m_queued = false; + completedSpecs++; + + StepLoadScreen(1); + } + } + + if (!slot.m_queued) + { + if (queuedSpecs < numAASpecs) + { + slot.m_queued = true; + slot.m_spec = specs + queuedSpecs; + slot.m_singleJobCompleted.store(0, std::memory_order_release); + slot.m_workerThread->AsyncExecuteTask(PreloadAATableThreadFunc, &slot); + + queuedSpecs++; + } + } + } + + StepLoadScreenRing(); + Delay(1, nullptr); + } + + for (unsigned int i = 0; i < cpus; i++) + slots[i].~PreloadAATableWorkSlot(); + + mm->Release(slots); + mutex->Destroy(); } } @@ -681,8 +930,9 @@ int gpAppMain() FlushResolutionChange(); InitLoadingWindow(); StepLoadScreen(2); - PreloadFonts(); StepLoadScreen(2); PreloadAATables(); + assert(isPrefsLoaded || loadScreenWindowPhase == 0); + PreloadFonts(); StepLoadScreen(2); #if defined COMPILEDEMO copyGood = true; diff --git a/GpCommon/IGpSystemServices.h b/GpCommon/IGpSystemServices.h index dfdec0b..a9f0d2c 100644 --- a/GpCommon/IGpSystemServices.h +++ b/GpCommon/IGpSystemServices.h @@ -28,6 +28,7 @@ public: virtual void Beep() const = 0; virtual bool IsTouchscreen() const = 0; virtual bool IsUsingMouseAsTouch() const = 0; + virtual bool IsFullscreenPreferred() const = 0; virtual bool IsTextInputObstructive() const = 0; virtual unsigned int GetCPUCount() const = 0; virtual void SetTextInputEnabled(bool isEnabled) = 0; diff --git a/PortabilityLayer/QDStandardPalette.cpp b/PortabilityLayer/QDStandardPalette.cpp index 173d7b3..1a6a096 100644 --- a/PortabilityLayer/QDStandardPalette.cpp +++ b/PortabilityLayer/QDStandardPalette.cpp @@ -1,4 +1,5 @@ #include "QDStandardPalette.h" +#include "IGpMutex.h" #include @@ -232,15 +233,22 @@ namespace PortabilityLayer return &ms_instance; } - const AntiAliasTable &StandardPalette::GetCachedPaletteAATable(const RGBAColor &color) + const AntiAliasTable &StandardPalette::GetCachedPaletteAATable(const RGBAColor &color, IGpMutex *mutex) { uint8_t rgb[3] = { color.r, color.g, color.b }; + if (mutex) + mutex->Lock(); + for (size_t i = 0; i < m_numCachedPaletteTables; i++) { const CachedPaletteTableEntry &entry = m_cachedPaletteTables[i]; if (entry.m_rgb[0] == rgb[0] && entry.m_rgb[1] == rgb[1] && entry.m_rgb[2] == rgb[2]) + { + if (mutex) + mutex->Unlock(); return entry.m_aaTable; + } } if (m_numCachedPaletteTables == kMaxCachedPaletteTables) @@ -250,18 +258,29 @@ namespace PortabilityLayer entry.m_rgb[0] = rgb[0]; entry.m_rgb[1] = rgb[1]; entry.m_rgb[2] = rgb[2]; + + if (mutex) + mutex->Unlock(); + entry.m_aaTable.GenerateForPalette(color, m_colors, 256, true); return entry.m_aaTable; } - const AntiAliasTable &StandardPalette::GetCachedToneAATable(uint8_t tone) + const AntiAliasTable &StandardPalette::GetCachedToneAATable(uint8_t tone, IGpMutex *mutex) { + if (mutex) + mutex->Lock(); + for (size_t i = 0; i < m_numCachedToneTables; i++) { const CachedToneTableEntry &entry = m_cachedToneTables[i]; if (entry.m_tone == tone) + { + if (mutex) + mutex->Unlock(); return entry.m_aaTable; + } } if (m_numCachedToneTables == kMaxCachedToneTables) @@ -269,6 +288,10 @@ namespace PortabilityLayer CachedToneTableEntry &entry = m_cachedToneTables[m_numCachedToneTables++]; entry.m_tone = tone; + + if (mutex) + mutex->Unlock(); + entry.m_aaTable.GenerateForSimpleScale(tone, true); return entry.m_aaTable; diff --git a/PortabilityLayer/QDStandardPalette.h b/PortabilityLayer/QDStandardPalette.h index f4c21d7..d43df32 100644 --- a/PortabilityLayer/QDStandardPalette.h +++ b/PortabilityLayer/QDStandardPalette.h @@ -3,6 +3,8 @@ #include "AntiAliasTable.h" #include "RGBAColor.h" +struct IGpMutex; + namespace PortabilityLayer { struct AntiAliasTable; @@ -21,8 +23,8 @@ namespace PortabilityLayer uint8_t MapColorLUT(uint8_t r, uint8_t g, uint8_t b) const; uint8_t MapColorLUT(const RGBAColor &color) const; - const AntiAliasTable &GetCachedPaletteAATable(const RGBAColor &color); - const AntiAliasTable &GetCachedToneAATable(uint8_t tone); + const AntiAliasTable &GetCachedPaletteAATable(const RGBAColor &color, IGpMutex *mutex = nullptr); + const AntiAliasTable &GetCachedToneAATable(uint8_t tone, IGpMutex *mutex = nullptr); static StandardPalette *GetInstance();