Skip to content

Commit

Permalink
Merge pull request #18511 from hrydgard/screen-manager-refactor
Browse files Browse the repository at this point in the history
Screen manager refactoring, preparation for running the game behind the pause screen
  • Loading branch information
hrydgard committed Dec 11, 2023
2 parents 7605c2e + ae1009c commit 9f01661
Show file tree
Hide file tree
Showing 28 changed files with 298 additions and 243 deletions.
2 changes: 1 addition & 1 deletion Common/GPU/Vulkan/VulkanFramebuffer.cpp
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions Common/System/System.h
Expand Up @@ -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,
Expand Down
3 changes: 1 addition & 2 deletions Common/UI/Context.cpp
Expand Up @@ -164,8 +164,7 @@ void UIContext::ActivateTopScissor() {
int w = std::max(0.0f, ceilf(scale_x * bounds.w));
int h = std::max(0.0f, ceilf(scale_y * bounds.h));
if (x < 0 || y < 0 || x + w > g_display.pixel_xres || y + h > g_display.pixel_yres) {
// This won't actually report outside a game, but we can try.
DEBUG_LOG(G3D, "UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d", screenTag_ ? screenTag_ : "N/A", x, y, w, h, g_display.pixel_xres, g_display.pixel_yres);
DEBUG_LOG(G3D, "UI scissor out of bounds: %d,%d-%d,%d / %d,%d", x, y, w, h, g_display.pixel_xres, g_display.pixel_yres);
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x >= g_display.pixel_xres) { x = g_display.pixel_xres - 1; }
Expand Down
6 changes: 0 additions & 6 deletions Common/UI/Context.h
Expand Up @@ -113,10 +113,6 @@ class UIContext {

void setUIAtlas(const std::string &name);

void SetScreenTag(const char *tag) {
screenTag_ = tag;
}

// TODO: Move to private.
const UI::Theme *theme;

Expand All @@ -142,6 +138,4 @@ class UIContext {

std::string lastUIAtlas_;
std::string UIAtlas_ = "ui_atlas.zim";

const char *screenTag_ = nullptr;
};
124 changes: 74 additions & 50 deletions Common/UI/Screen.cpp
Expand Up @@ -6,7 +6,6 @@
#include "Common/UI/UI.h"
#include "Common/UI/View.h"
#include "Common/UI/ViewGroup.h"
#include "Common/UI/IconCache.h"

#include "Common/Log.h"
#include "Common/TimeUtil.h"
Expand Down Expand Up @@ -61,11 +60,10 @@ 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();
}

g_iconCache.FrameUpdate();
}

void ScreenManager::switchToNext() {
Expand Down Expand Up @@ -139,7 +137,6 @@ void ScreenManager::axis(const AxisInput *axes, size_t count) {
void ScreenManager::deviceLost() {
for (auto &iter : stack_)
iter.screen->deviceLost();
g_iconCache.ClearTextures();
}

void ScreenManager::deviceRestored() {
Expand All @@ -159,52 +156,68 @@ void ScreenManager::resized() {

void ScreenManager::render() {
if (!stack_.empty()) {
switch (stack_.back().flags) {
case LAYER_TRANSPARENT:
if (stack_.size() == 1) {
ERROR_LOG(SYSTEM, "Can't have sidemenu over nothing");
break;
} else {
auto last = stack_.end();
auto iter = last;
iter--;
while (iter->flags == LAYER_TRANSPARENT) {
iter--;
}
auto first = iter;
_assert_(iter->screen);

// TODO: Make really sure that this "mismatched" pre/post only happens
// when screens are "compatible" (both are UIScreens, for example).
first->screen->preRender();
while (iter < last) {
iter->screen->render();
iter++;
}
stack_.back().screen->render();
if (overlayScreen_) {
overlayScreen_->render();
}
if (postRenderCb_) {
// Really can't render anything after this! Will crash the screenshot mechanism if we do.
postRenderCb_(getUIContext(), postRenderUserdata_);
}
first->screen->postRender();
break;
// Collect the screens to render
TinySet<Screen *, 6> layers;

// 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;
do {
--iter;
if (!coveringScreen) {
layers.push_back(iter->screen);
} else 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;
}
default:
_assert_(stack_.back().screen);
stack_.back().screen->preRender();
stack_.back().screen->render();
if (overlayScreen_) {
overlayScreen_->render();
if (iter->flags != LAYER_TRANSPARENT) {
coveringScreen = iter->screen;
}
if (postRenderCb_) {
// Really can't render anything after this! Will crash the screenshot mechanism if we do.
postRenderCb_(getUIContext(), postRenderUserdata_);
} while (iter != stack_.begin());

// 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;
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;
}
} else if (i == 0) {
mode = ScreenRenderMode::TOP;
} else {
mode = ScreenRenderMode::BEHIND;
}
stack_.back().screen->postRender();
break;
layers[i]->render(mode);
}

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

getUIContext()->Flush();

if (postRenderCb_) {
// Really can't render anything after this! Will crash the screenshot mechanism if we do.
postRenderCb_(getUIContext(), postRenderUserdata_);
}
} else {
ERROR_LOG(SYSTEM, "No current screen!");
Expand Down Expand Up @@ -235,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 {
Expand Down Expand Up @@ -370,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);
}
22 changes: 14 additions & 8 deletions Common/UI/Screen.h
Expand Up @@ -47,6 +47,15 @@ enum class ScreenFocusChange {
FOCUS_BECAME_TOP, // Became the top screen again
};

enum class ScreenRenderMode {
DEFAULT = 0,
FIRST = 1,
BACKGROUND = 2,
BEHIND = 4,
TOP = 8,
};
ENUM_CLASS_BITOPS(ScreenRenderMode);

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

virtual void onFinish(DialogResult reason) {}
virtual void update() {}
virtual void preRender() {}
virtual void render() {}
virtual void postRender() {}
virtual void render(ScreenRenderMode mode) {}
virtual void resized() {}
virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
virtual void sendMessage(UIMessage message, const char *value) {}
virtual void deviceLost() {}
virtual void deviceRestored() {}
virtual bool canBeBackground() const { return false; }
virtual bool wantBrightBackground() const { return false; } // special hack for DisplayLayoutScreen.

virtual void focusChanged(ScreenFocusChange focusChange);

Expand All @@ -78,10 +87,6 @@ class Screen {
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; }
Expand Down Expand Up @@ -153,7 +158,7 @@ class ScreenManager {
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_;

Expand All @@ -171,6 +176,7 @@ class ScreenManager {
const Screen *dialogFinished_ = nullptr;
DialogResult dialogResult_{};

Screen *backgroundScreen_ = nullptr;
Screen *overlayScreen_ = nullptr;

struct Layer {
Expand Down
28 changes: 13 additions & 15 deletions Common/UI/UIScreen.cpp
Expand Up @@ -193,7 +193,7 @@ void UIScreen::deviceRestored() {
root_->DeviceRestored(screenManager()->getDrawContext());
}

void UIScreen::preRender() {
void UIScreen::SetupViewport() {
using namespace Draw;
Draw::DrawContext *draw = screenManager()->getDrawContext();
_dbg_assert_(draw != nullptr);
Expand All @@ -212,28 +212,26 @@ void UIScreen::preRender() {
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
}

void UIScreen::postRender() {
screenManager()->getUIContext()->Flush();
}
void UIScreen::render(ScreenRenderMode mode) {
if (mode & ScreenRenderMode::FIRST) {
SetupViewport();
}

void UIScreen::render() {
DoRecreateViews();

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

uiContext->SetScreenTag(tag());
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();
}
}

Expand Down
10 changes: 6 additions & 4 deletions Common/UI/UIScreen.h
Expand Up @@ -36,9 +36,7 @@ class UIScreen : public Screen {
~UIScreen();

void update() override;
void preRender() override;
void render() override;
void postRender() override;
void render(ScreenRenderMode mode) override;
void deviceLost() override;
void deviceRestored() override;

Expand All @@ -61,7 +59,6 @@ class UIScreen : public Screen {

protected:
virtual void CreateViews() = 0;
virtual void DrawBackground(UIContext &dc) {}

void RecreateViews() override { recreateViews_ = true; }
bool UseVerticalLayout() const;
Expand All @@ -74,6 +71,8 @@ class UIScreen : public Screen {
bool ignoreInput_ = false;

protected:
virtual void DrawBackground(UIContext &ui) {}
void SetupViewport();
void DoRecreateViews();

bool recreateViews_ = true;
Expand Down Expand Up @@ -111,6 +110,9 @@ class PopupScreen : public UIDialogScreen {

void SetHasDropShadow(bool has) { hasDropShadow_ = has; }

// For the postproc param sliders on DisplayLayoutScreen
bool wantBrightBackground() const override { return !hasDropShadow_; }

protected:
virtual bool FillVertical() const { return false; }
virtual UI::Size PopupWidth() const { return 550; }
Expand Down
4 changes: 2 additions & 2 deletions UI/ControlMappingScreen.cpp
Expand Up @@ -713,8 +713,8 @@ void TouchTestScreen::axis(const AxisInput &axis) {
UpdateLogView();
}

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

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() override;
void render(ScreenRenderMode mode) override;

bool key(const KeyInput &key) override;
void axis(const AxisInput &axis) override;
Expand Down

0 comments on commit 9f01661

Please sign in to comment.