Skip to content

Commit

Permalink
Implement running the game in the background on the pause screen. Fix…
Browse files Browse the repository at this point in the history
… some bugs.
  • Loading branch information
hrydgard committed Dec 11, 2023
1 parent 9f01661 commit e8f7059
Show file tree
Hide file tree
Showing 25 changed files with 120 additions and 117 deletions.
17 changes: 8 additions & 9 deletions Common/UI/Screen.cpp
Expand Up @@ -154,7 +154,8 @@ void ScreenManager::resized() {
}
}

void ScreenManager::render() {
ScreenRenderFlags ScreenManager::render() {
ScreenRenderFlags flags = ScreenRenderFlags::NONE;
if (!stack_.empty()) {
// Collect the screens to render
TinySet<Screen *, 6> layers;
Expand All @@ -169,12 +170,12 @@ void ScreenManager::render() {
Screen *backgroundScreen = nullptr;
do {
--iter;
if (!coveringScreen) {
layers.push_back(iter->screen);
} else if (!backgroundScreen && iter->screen->canBeBackground()) {
if (!backgroundScreen && iter->screen->canBeBackground()) {
// There still might be a screen that wants to be background - generally the EmuScreen if present.
layers.push_back(iter->screen);
backgroundScreen = iter->screen;
} else if (!coveringScreen) {
layers.push_back(iter->screen);
}
if (iter->flags != LAYER_TRANSPARENT) {
coveringScreen = iter->screen;
Expand All @@ -194,9 +195,6 @@ void ScreenManager::render() {
if (i == (int)layers.size() - 1) {
// Bottom.
mode = ScreenRenderMode::FIRST;
if (layers[i] == backgroundScreen && coveringScreen != layers[i]) {
mode |= ScreenRenderMode::BACKGROUND;
}
if (i == 0) {
mode |= ScreenRenderMode::TOP;
}
Expand All @@ -205,12 +203,12 @@ void ScreenManager::render() {
} else {
mode = ScreenRenderMode::BEHIND;
}
layers[i]->render(mode);
flags |= layers[i]->render(mode);
}

if (overlayScreen_) {
// It doesn't care about mode.
overlayScreen_->render(ScreenRenderMode::TOP);
flags |= overlayScreen_->render(ScreenRenderMode::TOP);
}

getUIContext()->Flush();
Expand All @@ -224,6 +222,7 @@ void ScreenManager::render() {
}

processFinishDialog();
return flags;
}

void ScreenManager::getFocusPosition(float &x, float &y, float &z) {
Expand Down
11 changes: 8 additions & 3 deletions Common/UI/Screen.h
Expand Up @@ -50,12 +50,17 @@ enum class ScreenFocusChange {
enum class ScreenRenderMode {
DEFAULT = 0,
FIRST = 1,
BACKGROUND = 2,
BEHIND = 4,
TOP = 8,
};
ENUM_CLASS_BITOPS(ScreenRenderMode);

enum class ScreenRenderFlags {
NONE = 0,
HANDLED_THROTTLING = 1,
};
ENUM_CLASS_BITOPS(ScreenRenderFlags);

class Screen {
public:
Screen() : screenManager_(nullptr) { }
Expand All @@ -65,7 +70,7 @@ class Screen {

virtual void onFinish(DialogResult reason) {}
virtual void update() {}
virtual void render(ScreenRenderMode mode) {}
virtual ScreenRenderFlags render(ScreenRenderMode mode) = 0;
virtual void resized() {}
virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
virtual void sendMessage(UIMessage message, const char *value) {}
Expand Down Expand Up @@ -128,7 +133,7 @@ class ScreenManager {
postRenderUserdata_ = userdata;
}

void render();
ScreenRenderFlags render();
void resized();
void shutdown();

Expand Down
23 changes: 14 additions & 9 deletions Common/UI/UIScreen.cpp
Expand Up @@ -212,27 +212,32 @@ void UIScreen::SetupViewport() {
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
}

void UIScreen::render(ScreenRenderMode mode) {
ScreenRenderFlags UIScreen::render(ScreenRenderMode mode) {
if (mode & ScreenRenderMode::FIRST) {
SetupViewport();
}

DoRecreateViews();

UIContext &uiContext = *screenManager()->getUIContext();
if (root_) {
UIContext &uiContext = *screenManager()->getUIContext();

UI::LayoutViewHierarchy(uiContext, root_, ignoreInsets_);
}

uiContext.PushTransform({translation_, scale_, alpha_});
uiContext.PushTransform({translation_, scale_, alpha_});

uiContext.Begin();
DrawBackground(uiContext);
uiContext.Begin();
DrawBackground(uiContext);
if (root_) {
root_->Draw(uiContext);
uiContext.Flush();

uiContext.PopTransform();
}
uiContext.Flush();
DrawForeground(uiContext);
uiContext.Flush();

uiContext.PopTransform();

return ScreenRenderFlags::NONE;
}

TouchInput UIScreen::transformTouch(const TouchInput &touch) {
Expand Down
4 changes: 3 additions & 1 deletion Common/UI/UIScreen.h
Expand Up @@ -36,7 +36,7 @@ class UIScreen : public Screen {
~UIScreen();

void update() override;
void render(ScreenRenderMode mode) override;
ScreenRenderFlags render(ScreenRenderMode mode) override;
void deviceLost() override;
void deviceRestored() override;

Expand Down Expand Up @@ -72,6 +72,8 @@ class UIScreen : public Screen {

protected:
virtual void DrawBackground(UIContext &ui) {}
virtual void DrawForeground(UIContext &ui) {}

void SetupViewport();
void DoRecreateViews();

Expand Down
2 changes: 2 additions & 0 deletions Core/Config.cpp
Expand Up @@ -297,6 +297,8 @@ static const ConfigSetting generalSettings[] = {
ConfigSetting("EnablePlugins", &g_Config.bLoadPlugins, true, CfgFlag::PER_GAME),

ConfigSetting("IgnoreCompatSettings", &g_Config.sIgnoreCompatSettings, "", CfgFlag::PER_GAME | CfgFlag::REPORT),

ConfigSetting("RunBehindPauseMenu", &g_Config.bRunBehindPauseMenu, false, CfgFlag::DEFAULT),
};

static bool DefaultSasThread() {
Expand Down
3 changes: 2 additions & 1 deletion Core/Config.h
Expand Up @@ -99,9 +99,10 @@ struct Config {

// Not used on mobile devices.
bool bPauseExitsEmulator;

bool bPauseMenuExitsEmulator;

bool bRunBehindPauseMenu;

// Core
bool bIgnoreBadMemAccess;

Expand Down
4 changes: 0 additions & 4 deletions UI/BackgroundAudio.cpp
Expand Up @@ -302,10 +302,6 @@ void BackgroundAudio::SetGame(const Path &path) {
}

bool BackgroundAudio::Play() {
if (GetUIState() == UISTATE_INGAME) {
return false;
}

std::lock_guard<std::mutex> lock(mutex_);

// Immediately stop the sound if it is turned off while playing.
Expand Down
25 changes: 11 additions & 14 deletions UI/ControlMappingScreen.cpp
Expand Up @@ -713,27 +713,25 @@ void TouchTestScreen::axis(const AxisInput &axis) {
UpdateLogView();
}

void TouchTestScreen::render(ScreenRenderMode mode) {
UIDialogScreenWithGameBackground::render(mode);
UIContext *ui_context = screenManager()->getUIContext();
Bounds bounds = ui_context->GetLayoutBounds();
void TouchTestScreen::DrawForeground(UIContext &dc) {
Bounds bounds = dc.GetLayoutBounds();

ui_context->BeginNoTex();
dc.BeginNoTex();
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
ui_context->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
}
}
ui_context->Flush();
dc.Flush();

ui_context->Begin();
dc.Begin();

char buffer[4096];
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
ui_context->Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y);
ui_context->DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII);
dc.DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII);
}
}

Expand Down Expand Up @@ -762,8 +760,8 @@ void TouchTestScreen::render(ScreenRenderMode mode) {
// On Android, also add joystick debug data.


ui_context->DrawTextShadow(buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
ui_context->Flush();
dc.DrawTextShadow(buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
dc.Flush();
}

void RecreateActivity() {
Expand Down Expand Up @@ -799,8 +797,7 @@ UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) {

class Backplate : public UI::InertView {
public:
Backplate(float scale, UI::LayoutParams *layoutParams = nullptr) : InertView(layoutParams), scale_(scale) {
}
Backplate(float scale, UI::LayoutParams *layoutParams = nullptr) : InertView(layoutParams), scale_(scale) {}

void Draw(UIContext &dc) override {
for (float dy = 0.0f; dy <= 4.0f; dy += 1.0f) {
Expand Down
2 changes: 1 addition & 1 deletion UI/ControlMappingScreen.h
Expand Up @@ -147,7 +147,7 @@ class TouchTestScreen : public UIDialogScreenWithGameBackground {
}

void touch(const TouchInput &touch) override;
void render(ScreenRenderMode mode) override;
void DrawForeground(UIContext &dc) override;

bool key(const KeyInput &key) override;
void axis(const AxisInput &axis) override;
Expand Down
49 changes: 29 additions & 20 deletions UI/EmuScreen.cpp
Expand Up @@ -1438,12 +1438,17 @@ void EmuScreen::darken() {
}
}

void EmuScreen::render(ScreenRenderMode mode) {
ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) {
ScreenRenderFlags flags = ScreenRenderFlags::NONE;
using namespace Draw;

DrawContext *draw = screenManager()->getDrawContext();
if (!draw)
return flags; // shouldn't really happen but I've seen a suspicious stack trace..

if (mode & ScreenRenderMode::FIRST) {
// Actually, always gonna be first when it exists (?)

using namespace Draw;
DrawContext *draw = screenManager()->getDrawContext();
// Here we do NOT bind the backbuffer or clear the screen, unless non-buffered.
// The emuscreen is different than the others - we really want to allow the game to render to framebuffers
// before we ever bind the backbuffer for rendering. On mobile GPUs, switching back and forth between render
Expand Down Expand Up @@ -1471,27 +1476,21 @@ void EmuScreen::render(ScreenRenderMode mode) {
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
}

using namespace Draw;

DrawContext *thin3d = screenManager()->getDrawContext();
if (!thin3d)
return; // shouldn't really happen but I've seen a suspicious stack trace..

g_OSD.NudgeSidebar();

if (mode & ScreenRenderMode::TOP) {
System_Notify(SystemNotification::KEEP_SCREEN_AWAKE);
} else {
} else if (!g_Config.bRunBehindPauseMenu) {
// Not on top. Let's not execute, only draw the image.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
draw->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;
return flags;
}

if (invalid_) {
Expand All @@ -1502,9 +1501,9 @@ void EmuScreen::render(ScreenRenderMode mode) {
// It's possible this might be set outside PSP_RunLoopFor().
// In this case, we need to double check it here.
checkPowerDown();
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_Invalid");
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_Invalid");
renderUI();
return;
return flags;
}

// Freeze-frame functionality (loads a savestate on every frame).
Expand All @@ -1528,6 +1527,8 @@ void EmuScreen::render(ScreenRenderMode mode) {
PSP_BeginHostFrame();
PSP_RunLoopWhileState();

flags |= ScreenRenderFlags::HANDLED_THROTTLING;

// Hopefully coreState is now CORE_NEXTFRAME
switch (coreState) {
case CORE_NEXTFRAME:
Expand All @@ -1543,12 +1544,12 @@ void EmuScreen::render(ScreenRenderMode mode) {
// Clear to blue background screen
bool dangerousSettings = !Reporting::IsSupported();
uint32_t color = dangerousSettings ? 0xFF900050 : 0xFF900000;
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError");
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE, color }, "EmuScreen_RuntimeError");
// The info is drawn later in renderUI
} else {
// If we're stepping, it's convenient not to clear the screen entirely, so we copy display to output.
// This won't work in non-buffered, but that's fine.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_Stepping");
// Just to make sure.
if (PSP_IsInited()) {
gpu->CopyDisplayToOutput(true);
Expand All @@ -1570,12 +1571,19 @@ void EmuScreen::render(ScreenRenderMode mode) {
// This must happen after PSP_EndHostFrame so that things like push buffers are end-frame'd before we start destroying stuff.
if (checkPowerDown() || rebind) {
// Shutting down can end up ending the current render pass
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
}

if (!(mode & ScreenRenderMode::TOP)) {
// We're in run-behind mode, but we don't want to draw chat, debug UI and stuff.
// So, darken and bail here.
darken();
return flags;
}

if (hasVisibleUI()) {
// In most cases, this should already be bound and a no-op.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_UI");
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_UI");
cardboardDisableButton_->SetVisibility(g_Config.bEnableCardboardVR ? UI::V_VISIBLE : UI::V_GONE);
screenManager()->getUIContext()->BeginFrame();
renderUI();
Expand All @@ -1590,10 +1598,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()) {
draw->WipeQueue();
} else {
darken();
}
return flags;
}

bool EmuScreen::hasVisibleUI() {
Expand Down
2 changes: 1 addition & 1 deletion UI/EmuScreen.h
Expand Up @@ -42,7 +42,7 @@ class EmuScreen : public UIScreen {
const char *tag() const override { return "Emu"; }

void update() override;
void render(ScreenRenderMode mode) override;
ScreenRenderFlags render(ScreenRenderMode mode) override;
void dialogFinished(const Screen *dialog, DialogResult result) override;
void sendMessage(UIMessage message, const char *value) override;
void resized() override;
Expand Down

0 comments on commit e8f7059

Please sign in to comment.