Refactor UI background rendering. There's now a BackgroundScreen.

This commit is contained in:
Henrik Rydgård 2023-12-10 21:57:05 +01:00
parent 48d60d8613
commit 6d51fbc1e6
15 changed files with 171 additions and 120 deletions

View File

@ -291,7 +291,7 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
_dbg_assert_(!(isBackbuffer && multisample));
if (isBackbuffer) {
_dbg_assert_(key.depthLoadAction == VKRRenderPassLoadAction::CLEAR);
_dbg_assert_(key.depthLoadAction != VKRRenderPassLoadAction::KEEP);
}
if (multiview) {

View File

@ -224,6 +224,7 @@ enum class UIMessage {
REQUEST_GAME_PAUSE,
REQUEST_GAME_RESET,
REQUEST_GAME_STOP,
GAME_SELECTED,
SHOW_CONTROL_MAPPING,
SHOW_CHAT_SCREEN,
SHOW_DISPLAY_LAYOUT_EDITOR,

View File

@ -60,6 +60,7 @@ void ScreenManager::update() {
// NOTE: This is not a full UIScreen update, to avoid double global event processing.
overlayScreen_->update();
}
// The background screen doesn't need updating.
if (stack_.size()) {
stack_.back().screen->update();
}
@ -161,7 +162,8 @@ void ScreenManager::render() {
// Start at the end, collect screens to form the transparency stack.
// Then we'll iterate them in reverse order.
// Note that we skip the overlay screen, we handle it separately.
// Additionally, we pick up a "background" screen. Normally it will be either
// the EmuScreen or the actual global background screen.
auto iter = stack_.end();
Screen *coveringScreen = nullptr;
Screen *backgroundScreen = nullptr;
@ -179,7 +181,13 @@ void ScreenManager::render() {
}
} while (iter != stack_.begin());
// OK, now we iterate backwards over our little pile of screens.
// Confusing-looking expression, argh! Note the '_'
if (backgroundScreen_ && !backgroundScreen) {
layers.push_back(backgroundScreen_);
backgroundScreen = backgroundScreen_;
}
// OK, now we iterate backwards over our little pile of collected screens.
bool first = true;
for (int i = (int)layers.size() - 1; i >= 0; i--) {
ScreenRenderMode mode = ScreenRenderMode::DEFAULT;
@ -240,8 +248,13 @@ void ScreenManager::sendMessage(UIMessage message, const char *value) {
touch(input);
}
if (!stack_.empty())
if (backgroundScreen_) {
backgroundScreen_->sendMessage(message, value);
}
if (!stack_.empty()) {
stack_.back().screen->sendMessage(message, value);
}
}
Screen *ScreenManager::topScreen() const {
@ -375,10 +388,16 @@ void ScreenManager::processFinishDialog() {
}
}
void ScreenManager::SetOverlayScreen(Screen *screen) {
void ScreenManager::SetBackgroundOverlayScreens(Screen *backgroundScreen, Screen *overlayScreen) {
if (backgroundScreen_) {
delete backgroundScreen_;
}
backgroundScreen_ = backgroundScreen;
backgroundScreen_->setScreenManager(this);
if (overlayScreen_) {
delete overlayScreen_;
}
overlayScreen_ = screen;
overlayScreen_ = overlayScreen;
overlayScreen_->setScreenManager(this);
}

View File

@ -71,7 +71,8 @@ public:
virtual void sendMessage(UIMessage message, const char *value) {}
virtual void deviceLost() {}
virtual void deviceRestored() {}
virtual bool canBeBackground() { return false; }
virtual bool canBeBackground() const { return false; }
virtual bool wantBrightBackground() const { return false; } // special hack for DisplayLayoutScreen.
virtual void focusChanged(ScreenFocusChange focusChange);
@ -86,10 +87,6 @@ public:
ScreenManager *screenManager() { return screenManager_; }
void setScreenManager(ScreenManager *sm) { screenManager_ = sm; }
// This one is icky to use because you can't know what's in it until you know
// what screen it is.
virtual void *dialogData() { return 0; }
virtual const char *tag() const = 0;
virtual bool isTransparent() const { return false; }
@ -161,7 +158,7 @@ public:
void getFocusPosition(float &x, float &y, float &z);
// Will delete any existing overlay screen.
void SetOverlayScreen(Screen *screen);
void SetBackgroundOverlayScreens(Screen *backgroundScreen, Screen *overlayScreen);
std::recursive_mutex inputLock_;
@ -179,6 +176,7 @@ private:
const Screen *dialogFinished_ = nullptr;
DialogResult dialogResult_{};
Screen *backgroundScreen_ = nullptr;
Screen *overlayScreen_ = nullptr;
struct Layer {

View File

@ -193,41 +193,45 @@ void UIScreen::deviceRestored() {
root_->DeviceRestored(screenManager()->getDrawContext());
}
void UIScreen::SetupViewport() {
using namespace Draw;
Draw::DrawContext *draw = screenManager()->getDrawContext();
_dbg_assert_(draw != nullptr);
// Bind and clear the back buffer
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI");
screenManager()->getUIContext()->BeginFrame();
Draw::Viewport viewport;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = g_display.pixel_xres;
viewport.Height = g_display.pixel_yres;
viewport.MaxDepth = 1.0;
viewport.MinDepth = 0.0;
draw->SetViewport(viewport);
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
}
void UIScreen::render(ScreenRenderMode mode) {
if (mode & ScreenRenderMode::FIRST) {
using namespace Draw;
Draw::DrawContext *draw = screenManager()->getDrawContext();
_dbg_assert_(draw != nullptr);
// Bind and clear the back buffer
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "UI");
screenManager()->getUIContext()->BeginFrame();
Draw::Viewport viewport;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = g_display.pixel_xres;
viewport.Height = g_display.pixel_yres;
viewport.MaxDepth = 1.0;
viewport.MinDepth = 0.0;
draw->SetViewport(viewport);
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
SetupViewport();
}
DoRecreateViews();
if (root_) {
UIContext *uiContext = screenManager()->getUIContext();
UIContext &uiContext = *screenManager()->getUIContext();
UI::LayoutViewHierarchy(*uiContext, root_, ignoreInsets_);
UI::LayoutViewHierarchy(uiContext, root_, ignoreInsets_);
uiContext->PushTransform({translation_, scale_, alpha_});
uiContext.PushTransform({translation_, scale_, alpha_});
uiContext->Begin();
DrawBackground(*uiContext);
root_->Draw(*uiContext);
uiContext->Flush();
uiContext.Begin();
DrawBackground(uiContext);
root_->Draw(uiContext);
uiContext.Flush();
uiContext->PopTransform();
uiContext.PopTransform();
}
}

View File

@ -59,7 +59,6 @@ public:
protected:
virtual void CreateViews() = 0;
virtual void DrawBackground(UIContext &dc) {}
void RecreateViews() override { recreateViews_ = true; }
bool UseVerticalLayout() const;
@ -72,6 +71,8 @@ protected:
bool ignoreInput_ = false;
protected:
virtual void DrawBackground(UIContext &ui) {}
void SetupViewport();
void DoRecreateViews();
bool recreateViews_ = true;
@ -109,6 +110,9 @@ public:
void SetHasDropShadow(bool has) { hasDropShadow_ = has; }
// For the postproc param sliders on DisplayLayoutScreen
bool wantBrightBackground() const { return !hasDropShadow_; }
protected:
virtual bool FillVertical() const { return false; }
virtual UI::Size PopupWidth() const { return 550; }

View File

@ -117,19 +117,15 @@ private:
float startDisplayOffsetY_ = -1.0f;
};
DisplayLayoutScreen::DisplayLayoutScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename) {
// Show background at full brightness
darkenGameBackground_ = false;
forceTransparent_ = true;
}
DisplayLayoutScreen::DisplayLayoutScreen(const Path &filename) : UIDialogScreenWithGameBackground(filename) {}
void DisplayLayoutScreen::DrawBackground(UIContext &dc) {
if (PSP_IsInited() && !g_Config.bSkipBufferEffects) {
// We normally rely on the PSP screen.
UIDialogScreenWithGameBackground::DrawBackground(dc);
// We normally rely on the PSP screen showing through.
} else {
// But if it's not present (we're not in game, or skip buffer effects is used),
// we have to draw a substitute ourselves.
UIContext &dc = *screenManager()->getUIContext();
// TODO: Clean this up a bit, this GetScreenFrame/CenterDisplay combo is too common.
FRect screenFrame = GetScreenFrame(g_display.pixel_xres, g_display.pixel_yres);
@ -138,6 +134,7 @@ void DisplayLayoutScreen::DrawBackground(UIContext &dc) {
dc.Flush();
ImageID bg = ImageID("I_PSP_DISPLAY");
dc.Draw()->DrawImageStretch(bg, dc.GetBounds(), 0x7F000000);
dc.Draw()->DrawImageStretch(bg, FRectToBounds(rc), 0x7FFFFFFF);
}
}

View File

@ -32,18 +32,19 @@ public:
void dialogFinished(const Screen *dialog, DialogResult result) override;
void onFinish(DialogResult reason) override;
void DrawBackground(UIContext &dc) override;
void resized() override {
RecreateViews();
}
bool wantBrightBackground() const override { return true; }
const char *tag() const override { return "DisplayLayout"; }
protected:
UI::EventReturn OnPostProcShaderChange(UI::EventParams &e);
void sendMessage(UIMessage message, const char *value) override;
void DrawBackground(UIContext &dc) override;
private:
UI::ChoiceStrip *mode_ = nullptr;

View File

@ -361,6 +361,10 @@ void EmuScreen::bootGame(const Path &filename) {
loadingViewVisible_->Divert(UI::V_VISIBLE, 0.75f);
screenManager()->getDrawContext()->ResetStats();
if (bootPending_) {
System_PostUIMessage(UIMessage::GAME_SELECTED, filename.c_str());
}
}
void EmuScreen::bootComplete() {
@ -424,6 +428,8 @@ EmuScreen::~EmuScreen() {
PSP_Shutdown();
}
System_PostUIMessage(UIMessage::GAME_SELECTED, "");
g_OSD.ClearAchievementStuff();
SetExtraAssertInfo(nullptr);
@ -1409,9 +1415,32 @@ static void DrawFPS(UIContext *ctx, const Bounds &bounds) {
ctx->RebindTexture();
}
bool EmuScreen::canBeBackground() const {
if (g_Config.bSkipBufferEffects)
return false;
bool forceTransparent = false; // this needs to be true somehow on the display layout screen.
if (!g_Config.bTransparentBackground && !forceTransparent)
return false;
return true;
}
void EmuScreen::darken() {
if (!screenManager()->topScreen()->wantBrightBackground()) {
UIContext &dc = *screenManager()->getUIContext();
uint32_t color = GetBackgroundColorWithAlpha(dc);
dc.Begin();
dc.RebindTexture();
dc.FillRect(UI::Drawable(color), dc.GetBounds());
dc.Flush();
}
}
void EmuScreen::render(ScreenRenderMode mode) {
if (mode == ScreenRenderMode::FIRST) {
// Actually, always gonna be first (?)
if (mode & ScreenRenderMode::FIRST) {
// Actually, always gonna be first when it exists (?)
using namespace Draw;
DrawContext *draw = screenManager()->getDrawContext();
@ -1450,8 +1479,19 @@ void EmuScreen::render(ScreenRenderMode mode) {
g_OSD.NudgeSidebar();
if (screenManager()->topScreen() == this) {
if (mode & ScreenRenderMode::TOP) {
System_Notify(SystemNotification::KEEP_SCREEN_AWAKE);
} else {
// Not on top. Let's not execute, only draw the image.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
// Just to make sure.
if (PSP_IsInited() && !g_Config.bSkipBufferEffects) {
PSP_BeginHostFrame();
gpu->CopyDisplayToOutput(true);
PSP_EndHostFrame();
darken();
}
return;
}
if (invalid_) {
@ -1548,8 +1588,11 @@ void EmuScreen::render(ScreenRenderMode mode) {
}
if (mode & ScreenRenderMode::TOP) {
// TODO: Replace this with something else.
if (stopRender_)
thin3d->WipeQueue();
} else if (!screenManager()->topScreen()->wantBrightBackground()) {
darken();
}
}

View File

@ -46,6 +46,7 @@ public:
void dialogFinished(const Screen *dialog, DialogResult result) override;
void sendMessage(UIMessage message, const char *value) override;
void resized() override;
bool canBeBackground() const override;
// Note: Unlike your average boring UIScreen, here we override the Unsync* functions
// to get minimal latency and full control. We forward to UIScreen when needed.
@ -57,7 +58,7 @@ public:
bool key(const KeyInput &key) override;
protected:
void darken();
void focusChanged(ScreenFocusChange focusChange) override;
private:

View File

@ -51,12 +51,14 @@
GameScreen::GameScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) {
g_BackgroundAudio.SetGame(gamePath);
System_PostUIMessage(UIMessage::GAME_SELECTED, gamePath.ToString());
}
GameScreen::~GameScreen() {
if (CRC32string == "...") {
Reporting::CancelCRC();
}
System_PostUIMessage(UIMessage::GAME_SELECTED, "");
}
template <typename I> std::string int2hexstr(I w, size_t hex_len = sizeof(I) << 1) {

View File

@ -1389,7 +1389,6 @@ UI::EventReturn MainScreen::OnFullScreenToggle(UI::EventParams &e) {
}
void MainScreen::DrawBackground(UIContext &dc) {
UIScreenWithBackground::DrawBackground(dc);
if (highlightedGamePath_.empty() && prevHighlightedGamePath_.empty()) {
return;
}

View File

@ -368,36 +368,10 @@ uint32_t GetBackgroundColorWithAlpha(const UIContext &dc) {
return colorAlpha(colorBlend(dc.GetTheme().backgroundColor, 0, 0.5f), 0.65f); // 0.65 = 166 = A6
}
void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, float z, bool transparent, bool darkenBackground) {
void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, float z) {
using namespace Draw;
using namespace UI;
if (transparent && PSP_IsInited() && !g_Config.bSkipBufferEffects) {
gpu->CheckDisplayResized();
gpu->CheckConfigChanged();
gpu->CopyDisplayToOutput(true);
DrawContext *draw = dc.GetDrawContext();
Viewport viewport;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = g_display.pixel_xres;
viewport.Height = g_display.pixel_yres;
viewport.MaxDepth = 1.0;
viewport.MinDepth = 0.0;
draw->SetViewport(viewport);
dc.BeginFrame();
dc.RebindTexture();
dc.Begin();
if (darkenBackground) {
uint32_t color = GetBackgroundColorWithAlpha(dc);
dc.FillRect(UI::Drawable(color), dc.GetBounds());
dc.Flush();
}
return;
}
std::shared_ptr<GameInfo> ginfo;
if (!gamePath.empty())
ginfo = g_gameInfoCache->GetInfo(dc.GetDrawContext(), gamePath, GAMEINFO_WANTBG);
@ -455,21 +429,43 @@ void HandleCommonMessages(UIMessage message, const char *value, ScreenManager *m
}
}
void UIScreenWithBackground::DrawBackground(UIContext &dc) {
void BackgroundScreen::render(ScreenRenderMode mode) {
if (mode & ScreenRenderMode::FIRST) {
SetupViewport();
} else {
_dbg_assert_(false);
}
UIContext *uiContext = screenManager()->getUIContext();
uiContext->PushTransform({ translation_, scale_, alpha_ });
uiContext->Begin();
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
if (!gamePath_.empty()) {
::DrawGameBackground(*uiContext, gamePath_, x, y, z);
} else {
::DrawBackground(*uiContext, 1.0f, x, y, z);
}
uiContext->Flush();
uiContext->PopTransform();
}
void UIScreenWithGameBackground::DrawBackground(UIContext &dc) {
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
if (!gamePath_.empty()) {
DrawGameBackground(dc, gamePath_, x, y, z, (g_Config.bTransparentBackground || forceTransparent_), darkenGameBackground_);
} else {
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
void BackgroundScreen::sendMessage(UIMessage message, const char *value) {
switch (message) {
case UIMessage::GAME_SELECTED:
if (value && strlen(value)) {
gamePath_ = Path(value);
} else {
gamePath_.clear();
}
break;
default:
break;
}
}
@ -481,19 +477,6 @@ void UIScreenWithGameBackground::sendMessage(UIMessage message, const char *valu
}
}
void UIDialogScreenWithGameBackground::DrawBackground(UIContext &dc) {
using namespace UI;
using namespace Draw;
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
if (!gamePath_.empty()) {
DrawGameBackground(dc, gamePath_, x, y, z, (g_Config.bTransparentBackground || forceTransparent_), darkenGameBackground_);
} else {
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
}
}
void UIDialogScreenWithGameBackground::sendMessage(UIMessage message, const char *value) {
if (message == UIMessage::SHOW_SETTINGS && screenManager()->topScreen() == this) {
screenManager()->push(new GameSettingsScreen(gamePath_));
@ -506,13 +489,6 @@ void UIScreenWithBackground::sendMessage(UIMessage message, const char *value) {
HandleCommonMessages(message, value, screenManager(), this);
}
void UIDialogScreenWithBackground::DrawBackground(UIContext &dc) {
float x, y, z;
screenManager()->getFocusPosition(x, y, z);
::DrawBackground(dc, 1.0f, x, y, z);
dc.Flush();
}
void UIDialogScreenWithBackground::AddStandardBack(UI::ViewGroup *parent) {
using namespace UI;
auto di = GetI18NCategory(I18NCat::DIALOG);

View File

@ -36,19 +36,31 @@ void UIBackgroundShutdown();
inline void NoOpVoidBool(bool) {}
class BackgroundScreen : public UIScreen {
public:
void render(ScreenRenderMode mode) override;
void sendMessage(UIMessage message, const char *value) override;
private:
void CreateViews() override {}
const char *tag() const override { return "bg"; }
Path gamePath_;
};
// This doesn't have anything to do with the background anymore. It's just a PPSSPP UIScreen
// that knows how handle sendMessage properly. Same for all the below.
class UIScreenWithBackground : public UIScreen {
public:
UIScreenWithBackground() : UIScreen() {}
protected:
void DrawBackground(UIContext &dc) override;
void sendMessage(UIMessage message, const char *value) override;
};
class UIScreenWithGameBackground : public UIScreenWithBackground {
public:
UIScreenWithGameBackground(const std::string &gamePath)
: UIScreenWithBackground(), gamePath_(gamePath) {}
void DrawBackground(UIContext &dc) override;
UIScreenWithGameBackground(const Path &gamePath) : UIScreenWithBackground(), gamePath_(gamePath) {}
void sendMessage(UIMessage message, const char *value) override;
protected:
Path gamePath_;
@ -61,9 +73,7 @@ class UIDialogScreenWithBackground : public UIDialogScreen {
public:
UIDialogScreenWithBackground() : UIDialogScreen() {}
protected:
void DrawBackground(UIContext &dc) override;
void sendMessage(UIMessage message, const char *value) override;
void AddStandardBack(UI::ViewGroup *parent);
};
@ -71,13 +81,9 @@ class UIDialogScreenWithGameBackground : public UIDialogScreenWithBackground {
public:
UIDialogScreenWithGameBackground(const Path &gamePath)
: UIDialogScreenWithBackground(), gamePath_(gamePath) {}
void DrawBackground(UIContext &dc) override;
void sendMessage(UIMessage message, const char *value) override;
protected:
Path gamePath_;
bool forceTransparent_ = false;
bool darkenGameBackground_ = true;
};
class PromptScreen : public UIDialogScreenWithGameBackground {

View File

@ -798,7 +798,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
g_screenManager->switchScreen(new LogoScreen(AfterLogoScreen::DEFAULT));
}
g_screenManager->SetOverlayScreen(new OSDOverlayScreen());
g_screenManager->SetBackgroundOverlayScreens(new BackgroundScreen(), new OSDOverlayScreen());
// Easy testing
// screenManager->push(new GPUDriverTestScreen());