mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
Merge pull request #18511 from hrydgard/screen-manager-refactor
Screen manager refactoring, preparation for running the game behind the pause screen
This commit is contained in:
commit
9f01661774
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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; }
|
||||
|
@ -113,10 +113,6 @@ public:
|
||||
|
||||
void setUIAtlas(const std::string &name);
|
||||
|
||||
void SetScreenTag(const char *tag) {
|
||||
screenTag_ = tag;
|
||||
}
|
||||
|
||||
// TODO: Move to private.
|
||||
const UI::Theme *theme;
|
||||
|
||||
@ -142,6 +138,4 @@ private:
|
||||
|
||||
std::string lastUIAtlas_;
|
||||
std::string UIAtlas_ = "ui_atlas.zim";
|
||||
|
||||
const char *screenTag_ = nullptr;
|
||||
};
|
||||
|
@ -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"
|
||||
@ -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() {
|
||||
@ -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() {
|
||||
@ -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);
|
||||
// Collect the screens to render
|
||||
TinySet<Screen *, 6> layers;
|
||||
|
||||
// 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;
|
||||
// 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!");
|
||||
@ -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 {
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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) { }
|
||||
@ -56,14 +65,14 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
@ -78,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; }
|
||||
@ -153,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_;
|
||||
|
||||
@ -171,6 +176,7 @@ private:
|
||||
const Screen *dialogFinished_ = nullptr;
|
||||
DialogResult dialogResult_{};
|
||||
|
||||
Screen *backgroundScreen_ = nullptr;
|
||||
Screen *overlayScreen_ = nullptr;
|
||||
|
||||
struct Layer {
|
||||
|
@ -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);
|
||||
@ -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 &uiContext = *screenManager()->getUIContext();
|
||||
|
||||
uiContext->SetScreenTag(tag());
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,7 @@ public:
|
||||
~UIScreen();
|
||||
|
||||
void update() override;
|
||||
void preRender() override;
|
||||
void render() override;
|
||||
void postRender() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
void deviceLost() override;
|
||||
void deviceRestored() override;
|
||||
|
||||
@ -61,7 +59,6 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void CreateViews() = 0;
|
||||
virtual void DrawBackground(UIContext &dc) {}
|
||||
|
||||
void RecreateViews() override { recreateViews_ = true; }
|
||||
bool UseVerticalLayout() const;
|
||||
@ -74,6 +71,8 @@ protected:
|
||||
bool ignoreInput_ = false;
|
||||
|
||||
protected:
|
||||
virtual void DrawBackground(UIContext &ui) {}
|
||||
void SetupViewport();
|
||||
void DoRecreateViews();
|
||||
|
||||
bool recreateViews_ = true;
|
||||
@ -111,6 +110,9 @@ public:
|
||||
|
||||
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; }
|
||||
|
@ -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();
|
||||
|
||||
|
@ -147,7 +147,7 @@ public:
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
110
UI/EmuScreen.cpp
110
UI/EmuScreen.cpp
@ -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,45 +1415,62 @@ static void DrawFPS(UIContext *ctx, const Bounds &bounds) {
|
||||
ctx->RebindTexture();
|
||||
}
|
||||
|
||||
void EmuScreen::preRender() {
|
||||
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
|
||||
// targets is a mortal sin so it's very important that we don't bind the backbuffer unnecessarily here.
|
||||
// We only bind it in FramebufferManager::CopyDisplayToOutput (unless non-buffered)...
|
||||
// We do, however, start the frame in other ways.
|
||||
bool EmuScreen::canBeBackground() const {
|
||||
if (g_Config.bSkipBufferEffects)
|
||||
return false;
|
||||
|
||||
if ((g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) || Core_IsStepping()) {
|
||||
// We need to clear here already so that drawing during the frame is done on a clean slate.
|
||||
if (Core_IsStepping() && gpuStats.numFlips != 0) {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_BackBuffer");
|
||||
} else {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "EmuScreen_BackBuffer");
|
||||
}
|
||||
bool forceTransparent = false; // this needs to be true somehow on the display layout screen.
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
draw->SetTargetSize(g_display.pixel_xres, g_display.pixel_yres);
|
||||
}
|
||||
|
||||
void EmuScreen::postRender() {
|
||||
Draw::DrawContext *draw = screenManager()->getDrawContext();
|
||||
if (!draw)
|
||||
return;
|
||||
if (stopRender_)
|
||||
draw->WipeQueue();
|
||||
}
|
||||
void EmuScreen::render(ScreenRenderMode mode) {
|
||||
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
|
||||
// targets is a mortal sin so it's very important that we don't bind the backbuffer unnecessarily here.
|
||||
// We only bind it in FramebufferManager::CopyDisplayToOutput (unless non-buffered)...
|
||||
// We do, however, start the frame in other ways.
|
||||
|
||||
if ((g_Config.bSkipBufferEffects && !g_Config.bSoftwareRendering) || Core_IsStepping()) {
|
||||
// We need to clear here already so that drawing during the frame is done on a clean slate.
|
||||
if (Core_IsStepping() && gpuStats.numFlips != 0) {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE }, "EmuScreen_BackBuffer");
|
||||
} else {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 }, "EmuScreen_BackBuffer");
|
||||
}
|
||||
|
||||
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 EmuScreen::render() {
|
||||
using namespace Draw;
|
||||
|
||||
DrawContext *thin3d = screenManager()->getDrawContext();
|
||||
@ -1456,8 +1479,19 @@ void EmuScreen::render() {
|
||||
|
||||
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_) {
|
||||
@ -1552,6 +1586,14 @@ void EmuScreen::render() {
|
||||
} else {
|
||||
SetVRAppMode(screenManager()->topScreen() == this ? VRAppMode::VR_GAME_MODE : VRAppMode::VR_DIALOG_MODE);
|
||||
}
|
||||
|
||||
if (mode & ScreenRenderMode::TOP) {
|
||||
// TODO: Replace this with something else.
|
||||
if (stopRender_)
|
||||
thin3d->WipeQueue();
|
||||
} else if (!screenManager()->topScreen()->wantBrightBackground()) {
|
||||
darken();
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuScreen::hasVisibleUI() {
|
||||
|
@ -42,12 +42,11 @@ public:
|
||||
const char *tag() const override { return "Emu"; }
|
||||
|
||||
void update() override;
|
||||
void render() override;
|
||||
void preRender() override;
|
||||
void postRender() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
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.
|
||||
@ -59,7 +58,7 @@ public:
|
||||
bool key(const KeyInput &key) override;
|
||||
|
||||
protected:
|
||||
|
||||
void darken();
|
||||
void focusChanged(ScreenFocusChange focusChange) override;
|
||||
|
||||
private:
|
||||
|
@ -630,9 +630,9 @@ void GPUDriverTestScreen::ShaderTest() {
|
||||
}
|
||||
|
||||
|
||||
void GPUDriverTestScreen::render() {
|
||||
void GPUDriverTestScreen::render(ScreenRenderMode mode) {
|
||||
using namespace Draw;
|
||||
UIScreen::render();
|
||||
UIScreen::render(mode);
|
||||
|
||||
switch (tabHolder_->GetCurrentTab()) {
|
||||
case 0:
|
||||
|
@ -15,7 +15,7 @@ public:
|
||||
~GPUDriverTestScreen();
|
||||
|
||||
void CreateViews() override;
|
||||
void render() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
|
||||
const char *tag() const override { return "GPUDriverTest"; }
|
||||
|
||||
|
@ -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) {
|
||||
@ -274,8 +276,8 @@ UI::EventReturn GameScreen::OnDeleteConfig(UI::EventParams &e)
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
void GameScreen::render() {
|
||||
UIScreen::render();
|
||||
void GameScreen::render(ScreenRenderMode mode) {
|
||||
UIScreen::render(mode);
|
||||
|
||||
auto ga = GetI18NCategory(I18NCat::GAME);
|
||||
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
|
||||
void update() override;
|
||||
|
||||
void render() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
|
||||
const char *tag() const override { return "Game"; }
|
||||
|
||||
|
@ -327,6 +327,8 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings)
|
||||
int max_res = std::min(max_res_temp, (int)ARRAY_SIZE(deviceResolutions));
|
||||
UI::PopupMultiChoice *hwscale = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAndroidHwScale, gr->T("Display Resolution (HW scaler)"), deviceResolutions, 0, max_res, I18NCat::GRAPHICS, screenManager()));
|
||||
hwscale->OnChoice.Add([](UI::EventParams &) {
|
||||
System_PostUIMessage(UIMessage::GPU_RENDER_RESIZED);
|
||||
System_PostUIMessage(UIMessage::GPU_DISPLAY_RESIZED);
|
||||
System_RecreateActivity();
|
||||
return UI::EVENT_DONE;
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -52,11 +52,11 @@ protected:
|
||||
|
||||
void dialogFinished(const Screen *dialog, DialogResult result) override;
|
||||
void update() override;
|
||||
void render() override {
|
||||
void render(ScreenRenderMode mode) override {
|
||||
// Simple anti-flicker due to delayed finish.
|
||||
if (!done_) {
|
||||
// render as usual.
|
||||
UIDialogScreenWithBackground::render();
|
||||
UIDialogScreenWithBackground::render(mode);
|
||||
} else {
|
||||
// no render. black frame insertion is better than flicker.
|
||||
}
|
||||
|
@ -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);
|
||||
@ -756,10 +732,10 @@ void LogoScreen::touch(const TouchInput &touch) {
|
||||
}
|
||||
}
|
||||
|
||||
void LogoScreen::render() {
|
||||
void LogoScreen::render(ScreenRenderMode mode) {
|
||||
using namespace Draw;
|
||||
|
||||
UIScreen::render();
|
||||
UIScreen::render(mode);
|
||||
UIContext &dc = *screenManager()->getUIContext();
|
||||
|
||||
const Bounds &bounds = dc.GetBounds();
|
||||
@ -895,8 +871,8 @@ void CreditsScreen::update() {
|
||||
UpdateUIState(UISTATE_MENU);
|
||||
}
|
||||
|
||||
void CreditsScreen::render() {
|
||||
UIScreen::render();
|
||||
void CreditsScreen::render(ScreenRenderMode mode) {
|
||||
UIScreen::render(mode);
|
||||
|
||||
auto cr = GetI18NCategory(I18NCat::PSPCREDITS);
|
||||
|
||||
|
@ -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 {
|
||||
@ -140,7 +146,7 @@ public:
|
||||
bool key(const KeyInput &key) override;
|
||||
void touch(const TouchInput &touch) override;
|
||||
void update() override;
|
||||
void render() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
void sendMessage(UIMessage message, const char *value) override;
|
||||
void CreateViews() override {}
|
||||
|
||||
@ -158,7 +164,7 @@ class CreditsScreen : public UIDialogScreenWithBackground {
|
||||
public:
|
||||
CreditsScreen();
|
||||
void update() override;
|
||||
void render() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
|
||||
void CreateViews() override;
|
||||
|
||||
|
@ -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());
|
||||
@ -956,6 +956,7 @@ void NativeShutdownGraphics() {
|
||||
if (g_screenManager) {
|
||||
g_screenManager->deviceLost();
|
||||
}
|
||||
g_iconCache.ClearTextures();
|
||||
|
||||
if (gpu)
|
||||
gpu->DeviceLost();
|
||||
@ -1104,7 +1105,6 @@ void NativeFrame(GraphicsContext *graphicsContext) {
|
||||
Achievements::Idle();
|
||||
|
||||
g_DownloadManager.Update();
|
||||
g_screenManager->update();
|
||||
|
||||
g_Discord.Update();
|
||||
g_BackgroundAudio.Play();
|
||||
@ -1124,6 +1124,10 @@ void NativeFrame(GraphicsContext *graphicsContext) {
|
||||
g_BackgroundAudio.Update();
|
||||
}
|
||||
|
||||
g_iconCache.FrameUpdate();
|
||||
|
||||
g_screenManager->update();
|
||||
|
||||
// Apply the UIContext bounds as a 2D transformation matrix.
|
||||
// TODO: This should be moved into the draw context...
|
||||
Matrix4x4 ortho = ComputeOrthoMatrix(g_display.dp_xres, g_display.dp_yres);
|
||||
|
@ -517,8 +517,8 @@ void OSDOverlayScreen::CreateViews() {
|
||||
osmView_ = root_->Add(new OnScreenMessagesView(new UI::AnchorLayoutParams(0.0f, 0.0f, 0.0f, 0.0f)));
|
||||
}
|
||||
|
||||
void OSDOverlayScreen::render() {
|
||||
UIScreen::render();
|
||||
void OSDOverlayScreen::render(ScreenRenderMode mode) {
|
||||
UIScreen::render(mode);
|
||||
|
||||
DebugOverlay debugOverlay = (DebugOverlay)g_Config.iDebugOverlay;
|
||||
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
bool UnsyncTouch(const TouchInput &touch) override;
|
||||
|
||||
void CreateViews() override;
|
||||
void render() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
|
@ -166,26 +166,29 @@ ReportScreen::ReportScreen(const Path &gamePath)
|
||||
ratingEnabled_ = enableReporting_;
|
||||
}
|
||||
|
||||
void ReportScreen::postRender() {
|
||||
// We do this after render because we need it to be within the frame (so the screenshot works).
|
||||
// We could do it mid frame, but then we have to reapply viewport/scissor.
|
||||
if (!tookScreenshot_) {
|
||||
Path path = GetSysDirectory(DIRECTORY_SCREENSHOT);
|
||||
if (!File::Exists(path)) {
|
||||
File::CreateDir(path);
|
||||
}
|
||||
screenshotFilename_ = path / ".reporting.jpg";
|
||||
if (TakeGameScreenshot(screenshotFilename_, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, 4)) {
|
||||
// Redo the views already, now with a screenshot included.
|
||||
RecreateViews();
|
||||
} else {
|
||||
// Good news (?), the views are good as-is without a screenshot.
|
||||
screenshotFilename_.clear();
|
||||
}
|
||||
tookScreenshot_ = true;
|
||||
}
|
||||
void ReportScreen::render(ScreenRenderMode mode) {
|
||||
UIScreen::render(mode);
|
||||
|
||||
UIDialogScreenWithGameBackground::postRender();
|
||||
if (mode & ScreenRenderMode::TOP) {
|
||||
|
||||
// We do this after render because we need it to be within the frame (so the screenshot works).
|
||||
// We could do it mid frame, but then we have to reapply viewport/scissor.
|
||||
if (!tookScreenshot_) {
|
||||
Path path = GetSysDirectory(DIRECTORY_SCREENSHOT);
|
||||
if (!File::Exists(path)) {
|
||||
File::CreateDir(path);
|
||||
}
|
||||
screenshotFilename_ = path / ".reporting.jpg";
|
||||
if (TakeGameScreenshot(screenshotFilename_, ScreenshotFormat::JPG, SCREENSHOT_DISPLAY, nullptr, nullptr, 4)) {
|
||||
// Redo the views already, now with a screenshot included.
|
||||
RecreateViews();
|
||||
} else {
|
||||
// Good news (?), the views are good as-is without a screenshot.
|
||||
screenshotFilename_.clear();
|
||||
}
|
||||
tookScreenshot_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReportScreen::update() {
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
const char *tag() const override { return "Report"; }
|
||||
|
||||
protected:
|
||||
void postRender() override;
|
||||
void render(ScreenRenderMode mode) override;
|
||||
void update() override;
|
||||
void resized() override;
|
||||
void CreateViews() override;
|
||||
|
Loading…
Reference in New Issue
Block a user