mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-02 02:35:24 +02:00
div.s now maintains fcr31's Cause.Z and (when the trap is masked) sticky Flag.Z bits, in the standard MIPS bit positions. Only a finite non-zero dividend counts, so 0/0, inf/0 and NaN operands are excluded per IEEE 754. When the guest has the trap unmasked, the new Core_FPUException() reports it with the usual module suffix and MIPS call stack, and fd is left unwritten as hardware would. That's gated behind a new developer setting, off by default: PSP threads start with fcr31 = 0x00000e00, i.e. three of the traps already enabled, and games divide by zero without meaning anything by it. The fcr31 bits are updated either way, so what the game reads back doesn't depend on the setting. Interpreter only - the JITs are unchanged, and none of this is reachable under them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9fKXvYBrnqtp1QQGaGWVv
529 lines
17 KiB
C++
529 lines
17 KiB
C++
#include "Common/Render/DrawBuffer.h"
|
|
#include "Common/GPU/thin3d.h"
|
|
#include "Common/System/System.h"
|
|
#include "Common/Data/Text/I18n.h"
|
|
#include "Common/CPUDetect.h"
|
|
#include "Common/StringUtils.h"
|
|
#include "Common/Data/Text/StringWriter.h"
|
|
|
|
#include "Core/MIPS/MIPS.h"
|
|
#include "Core/HW/Display.h"
|
|
#include "Core/FrameTiming.h"
|
|
#include "Core/HLE/sceSas.h"
|
|
#include "Core/HLE/sceKernel.h"
|
|
#include "Core/HLE/scePower.h"
|
|
#include "Core/HLE/Plugins.h"
|
|
#include "Core/ControlMapper.h"
|
|
#include "Core/Config.h"
|
|
#include "Core/MemFault.h"
|
|
#include "Core/Reporting.h"
|
|
#include "Core/CwCheat.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/ELF/ParamSFO.h"
|
|
#include "Core/System.h"
|
|
#include "Core/Util/GameDB.h"
|
|
#include "GPU/GPU.h"
|
|
#include "GPU/GPUCommon.h"
|
|
// TODO: This should be moved here or to Common, doesn't belong in /GPU
|
|
#include "GPU/Vulkan/DebugVisVulkan.h"
|
|
#include "GPU/Common/FramebufferManagerCommon.h"
|
|
|
|
#include "UI/DevScreens.h"
|
|
#include "UI/DebugOverlay.h"
|
|
|
|
// For std::max
|
|
#include <algorithm>
|
|
|
|
static void DrawDebugStats(UIContext *ctx, const Bounds &bounds) {
|
|
FontID ubuntu24("UBUNTU24");
|
|
|
|
float left = std::max(bounds.w / 2 - 20.0f, 550.0f);
|
|
float right = bounds.w - left - 20.0f;
|
|
|
|
char statbuf[4096];
|
|
|
|
ctx->Flush();
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(.7f, .7f);
|
|
|
|
StringWriter w(statbuf, sizeof(statbuf));
|
|
w.F("Kernel processing time: %0.2f ms\n"
|
|
"Slowest syscall: %s : %0.2f ms\n"
|
|
"Most active syscall: %s : %0.2f ms\n",
|
|
kernelStats.msInSyscalls * 1000.0f,
|
|
kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)",
|
|
kernelStats.slowestSyscallTime * 1000.0f,
|
|
kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)",
|
|
kernelStats.summedSlowestSyscallTime * 1000.0f);
|
|
|
|
__DisplayGetDebugStats(w);
|
|
|
|
ctx->Draw()->DrawTextRect(ubuntu24, w.as_view(), bounds.x + 11, bounds.y + 31, left, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->DrawTextRect(ubuntu24, w.as_view(), bounds.x + 10, bounds.y + 30, left, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
|
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
ctx->Flush();
|
|
ctx->RebindTexture();
|
|
}
|
|
|
|
static void DrawAudioDebugStats(UIContext *ctx, const Bounds &bounds) {
|
|
FontID ubuntu24("UBUNTU24");
|
|
|
|
char statbuf[4096];
|
|
System_AudioGetDebugStats(statbuf, sizeof(statbuf));
|
|
|
|
ctx->Flush();
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(0.5f, 0.5f);
|
|
ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 11, bounds.y + 31, bounds.w - 20, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 10, bounds.y + 30, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
|
|
|
float left = std::max(bounds.w / 2 - 20.0f, 500.0f);
|
|
|
|
__SasGetDebugStats(statbuf, sizeof(statbuf));
|
|
ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + left + 21, bounds.y + 31, bounds.w - left, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + left + 20, bounds.y + 30, bounds.w - left, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
|
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
|
|
ctx->Flush();
|
|
ctx->RebindTexture();
|
|
}
|
|
|
|
static void DrawControlDebug(UIContext *ctx, const ControlMapper &mapper, const Bounds &bounds) {
|
|
FontID ubuntu24("UBUNTU24");
|
|
|
|
char statbuf[2048];
|
|
StringWriter w(statbuf, sizeof(statbuf));
|
|
mapper.GetDebugString(w);
|
|
|
|
ctx->Flush();
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(0.5f, 0.5f);
|
|
ctx->Draw()->DrawTextRect(ubuntu24, w.as_view(), bounds.x + 11, bounds.y + 31, bounds.w - 20, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->DrawTextRect(ubuntu24, w.as_view(), bounds.x + 10, bounds.y + 30, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
ctx->Flush();
|
|
ctx->RebindTexture();
|
|
}
|
|
|
|
static void DrawFrameTimes(UIContext *ctx, const Bounds &bounds) {
|
|
FontID ubuntu24("UBUNTU24");
|
|
float *sleepHistory;
|
|
int valid, pos;
|
|
float *history = __DisplayGetFrameTimes(&valid, &pos, &sleepHistory);
|
|
int scale = 7000;
|
|
int width = 600;
|
|
|
|
ctx->Flush();
|
|
ctx->BeginNoTex();
|
|
int bottom = bounds.y2();
|
|
for (int i = 0; i < valid; ++i) {
|
|
double activeTime = history[i] - sleepHistory[i];
|
|
ctx->Draw()->vLine(bounds.x + i, bottom, bottom - activeTime * scale, 0xFF3FFF3F);
|
|
ctx->Draw()->vLine(bounds.x + i, bottom - activeTime * scale, bottom - history[i] * scale, 0x7F3FFF3F);
|
|
}
|
|
ctx->Draw()->vLine(bounds.x + pos, bottom, bottom - 512, 0xFFff3F3f);
|
|
|
|
ctx->Draw()->hLine(bounds.x, bottom - 0.0333 * scale, bounds.x + width, 0xFF3f3Fff);
|
|
ctx->Draw()->hLine(bounds.x, bottom - 0.0167 * scale, bounds.x + width, 0xFF3f3Fff);
|
|
|
|
ctx->Flush();
|
|
ctx->Begin();
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(0.5f, 0.5f);
|
|
ctx->Draw()->DrawText(ubuntu24, "33.3ms", bounds.x + width, bottom - 0.0333 * scale, 0xFF3f3Fff, ALIGN_BOTTOMLEFT | FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->DrawText(ubuntu24, "16.7ms", bounds.x + width, bottom - 0.0167 * scale, 0xFF3f3Fff, ALIGN_BOTTOMLEFT | FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
ctx->Flush();
|
|
ctx->RebindTexture();
|
|
}
|
|
|
|
static void DrawFrameTiming(UIContext *ctx, const Bounds &bounds) {
|
|
FontID ubuntu24("UBUNTU24");
|
|
|
|
char statBuf[1024]{};
|
|
|
|
Draw::DrawContext *draw = ctx->GetDrawContext();
|
|
|
|
ctx->Flush();
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(0.5f, 0.5f);
|
|
|
|
// NOTE: This is not necessarily the same as the actual present mode.
|
|
snprintf(statBuf, sizeof(statBuf),
|
|
"Presentation mode: %s Needs skip for ff: %s\n"
|
|
"Actual presentation mode: %s",
|
|
Draw::PresentModeToString(g_frameTiming.PresentMode()),
|
|
g_frameTiming.FastForwardNeedsSkipFlip() ? "true" : "false",
|
|
Draw::PresentModeToString(draw->GetCurrentPresentMode()));
|
|
|
|
ctx->Draw()->DrawTextRect(ubuntu24, statBuf, bounds.x + 10, bounds.y + 50, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
size_t curIndex = i + 6;
|
|
size_t prevIndex = i + 7;
|
|
|
|
FrameTimeData data = ctx->GetDrawContext()->FrameTimeHistory().Back(curIndex);
|
|
FrameTimeData prevData = ctx->GetDrawContext()->FrameTimeHistory().Back(prevIndex);
|
|
if (data.frameBegin == 0.0) {
|
|
snprintf(statBuf, sizeof(statBuf), "(No frame time data)");
|
|
} else {
|
|
double stride = data.frameBegin - prevData.frameBegin;
|
|
double fenceLatency_s = data.afterFenceWait - data.frameBegin;
|
|
double submitLatency_s = data.firstSubmit - data.frameBegin;
|
|
double queuePresentLatency_s = data.queuePresent - data.frameBegin;
|
|
double actualPresentLatency_s = data.actualPresent - data.frameBegin;
|
|
double presentMargin = data.presentMargin;
|
|
double computedMargin = data.actualPresent - data.queuePresent;
|
|
|
|
char presentStats[256] = "";
|
|
if (data.actualPresent != 0.0) {
|
|
snprintf(presentStats, sizeof(presentStats),
|
|
"* Present: %0.1f ms\n"
|
|
"* Margin: %0.1f ms\n"
|
|
"* Margin(c): %0.1f ms\n",
|
|
actualPresentLatency_s * 1000.0,
|
|
presentMargin * 1000.0,
|
|
computedMargin * 1000.0);
|
|
}
|
|
snprintf(statBuf, sizeof(statBuf),
|
|
"* Stride: %0.1f (waits: %d)\n"
|
|
"%llu: From start:\n"
|
|
"* Past fence: %0.1f ms\n"
|
|
"* Submit #1: %0.1f ms\n"
|
|
"* Queue-p: %0.1f ms\n"
|
|
"%s",
|
|
stride * 1000.0,
|
|
data.waitCount,
|
|
(long long)data.frameId,
|
|
fenceLatency_s * 1000.0,
|
|
submitLatency_s * 1000.0,
|
|
queuePresentLatency_s * 1000.0,
|
|
presentStats
|
|
);
|
|
}
|
|
ctx->Draw()->DrawTextRect(ubuntu24, statBuf, bounds.x + 10 + i * 150, bounds.y + 150, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
|
}
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
ctx->Flush();
|
|
ctx->RebindTexture();
|
|
}
|
|
|
|
void DrawFramebufferList(UIContext *ctx, GPUCommon *gpu, const Bounds &bounds) {
|
|
if (!gpu) {
|
|
return;
|
|
}
|
|
FontID ubuntu24("UBUNTU24");
|
|
auto list = gpu->GetFramebufferList();
|
|
ctx->Flush();
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(0.7f, 0.7f);
|
|
|
|
int i = 0;
|
|
for (const VirtualFramebuffer *vfb : list) {
|
|
char buf[512];
|
|
snprintf(buf, sizeof(buf), "%08x (Z %08x): %dx%d (stride %d, %d)",
|
|
vfb->fb_address, vfb->z_address, vfb->width, vfb->height, vfb->fb_stride, vfb->z_stride);
|
|
ctx->Draw()->DrawTextRect(ubuntu24, buf, bounds.x + 10, bounds.y + 20 + i * 50, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
|
|
i++;
|
|
}
|
|
ctx->Flush();
|
|
}
|
|
|
|
void DrawControlMapperOverlay(UIContext *ctx, const Bounds &bounds, const ControlMapper &controlMapper) {
|
|
DrawControlDebug(ctx, controlMapper, ctx->GetLayoutBounds(ViewLayoutMode::ApplyInsets, false));
|
|
}
|
|
|
|
void DrawDebugOverlay(UIContext *ctx, const Bounds &bounds, DebugOverlay overlay) {
|
|
bool inGame = GetUIState() == UISTATE_INGAME;
|
|
|
|
const Bounds layoutBounds = ctx->GetLayoutBounds(ViewLayoutMode::ApplyInsets, false);
|
|
|
|
switch (overlay) {
|
|
case DebugOverlay::DEBUG_STATS:
|
|
if (inGame)
|
|
DrawDebugStats(ctx, layoutBounds);
|
|
break;
|
|
case DebugOverlay::FRAME_GRAPH:
|
|
if (inGame)
|
|
DrawFrameTimes(ctx, layoutBounds);
|
|
break;
|
|
case DebugOverlay::FRAME_TIMING:
|
|
DrawFrameTiming(ctx, layoutBounds);
|
|
break;
|
|
case DebugOverlay::Audio:
|
|
DrawAudioDebugStats(ctx, layoutBounds);
|
|
break;
|
|
#if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH)
|
|
case DebugOverlay::GPU_PROFILE:
|
|
if (inGame && (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL)) {
|
|
DrawGPUProfilerVis(ctx, gpu);
|
|
}
|
|
break;
|
|
case DebugOverlay::GPU_ALLOCATOR:
|
|
if (inGame && (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL)) {
|
|
DrawGPUMemoryVis(ctx, gpu);
|
|
}
|
|
break;
|
|
#endif
|
|
case DebugOverlay::FRAMEBUFFER_LIST:
|
|
if (inGame)
|
|
DrawFramebufferList(ctx, gpu, layoutBounds);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const char *CPUCoreAsString(int core) {
|
|
switch (core) {
|
|
case 0: return "Interpreter";
|
|
case 1: return "JIT";
|
|
case 2: return "IR Interpreter";
|
|
case 3: return "JIT using IR";
|
|
default: return "N/A";
|
|
}
|
|
}
|
|
|
|
void DrawCrashDump(UIContext *ctx, const Path &gamePath) {
|
|
const MIPSExceptionInfo &info = Core_GetExceptionInfo();
|
|
|
|
auto sy = GetI18NCategory(I18NCat::SYSTEM);
|
|
FontID ubuntu24("UBUNTU24");
|
|
std::string discID = g_paramSFO.GetDiscID();
|
|
|
|
std::string activeFlags = PSP_CoreParameter().compat.GetActiveFlagsString();
|
|
if (activeFlags.empty()) {
|
|
activeFlags = "(no compat flags active)";
|
|
}
|
|
|
|
int x = 20 + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT);
|
|
int y = 20 + System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP);
|
|
|
|
ctx->Flush();
|
|
if (ctx->Draw()->GetFontAtlas()->getFont(ubuntu24))
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(1.1f, 1.1f);
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, sy->T_cstr("Game crashed"), x, y, 0xFFFFFFFF);
|
|
|
|
char statbuf[4096];
|
|
char versionString[256];
|
|
snprintf(versionString, sizeof(versionString), "%s", PPSSPP_GIT_VERSION);
|
|
|
|
bool checkingISO = false;
|
|
bool isoOK = false;
|
|
char crcStr[50]{};
|
|
|
|
switch (PSP_CoreParameter().fileType) {
|
|
case IdentifiedFileType::PSP_ISO:
|
|
case IdentifiedFileType::PSP_ISO_NP:
|
|
{
|
|
if (Reporting::HasCRC(gamePath)) {
|
|
u32 crc = Reporting::RetrieveCRC(gamePath);
|
|
std::vector<GameDBInfo> dbInfos;
|
|
if (discID.size() >= 9 && g_gameDB.GetGameInfos(discID, &dbInfos)) {
|
|
for (auto &dbInfo : dbInfos) {
|
|
if (dbInfo.crc == crc) {
|
|
isoOK = true;
|
|
}
|
|
}
|
|
}
|
|
snprintf(crcStr, sizeof(crcStr), "CRC: %08x %s\n", crc, isoOK ? "(Known good!)" : "(not identified)");
|
|
} else {
|
|
// Queue it for calculation, we want it!
|
|
// It's OK to call this repeatedly until we have it, which is natural here.
|
|
Reporting::QueueCRC(gamePath);
|
|
checkingISO = true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// Don't show ISO warning for other file types.
|
|
isoOK = true;
|
|
break;
|
|
};
|
|
|
|
// TODO: Draw a lot more information. Full register set, and so on.
|
|
|
|
#ifdef _DEBUG
|
|
const char * const build = "debug";
|
|
#else
|
|
const char * const build = "release";
|
|
#endif
|
|
|
|
std::string sysName = System_GetProperty(SYSPROP_NAME);
|
|
int sysVersion = System_GetPropertyInt(SYSPROP_SYSTEMVERSION);
|
|
|
|
// First column
|
|
y += 65;
|
|
|
|
int columnWidth = (ctx->GetBounds().w - x - 10) / 2;
|
|
int height = ctx->GetBounds().h;
|
|
|
|
ctx->PushScissor(Bounds(x, y, columnWidth, height));
|
|
|
|
// INFO_LOG(Log::System, "DrawCrashDump (%d %d %d %d)", x, y, columnWidth, height);
|
|
|
|
snprintf(statbuf, sizeof(statbuf), R"(%s
|
|
%s (%s)
|
|
%s (%s)
|
|
%s v%d (%s)
|
|
%s
|
|
)",
|
|
ExceptionTypeAsString(info.type),
|
|
discID.c_str(), g_paramSFO.GetValueString("TITLE").c_str(),
|
|
versionString, build,
|
|
sysName.c_str(), sysVersion, GetCompilerABI(),
|
|
crcStr
|
|
);
|
|
|
|
ctx->Draw()->SetFontScale(.7f, .7f);
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
y += 160;
|
|
|
|
if (info.type == MIPSExceptionType::MEMORY) {
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
Access: %s at %08x (sz: %d)
|
|
PC: %08x
|
|
%s)",
|
|
MemoryExceptionTypeAsString(info.memory_type),
|
|
info.address,
|
|
info.accessSize,
|
|
info.pc,
|
|
info.info.c_str());
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
y += 180;
|
|
} else if (info.type == MIPSExceptionType::BAD_EXEC_ADDR) {
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
Destination: %s to %08x
|
|
PC: %08x
|
|
RA: %08x)",
|
|
ExecExceptionTypeAsString(info.exec_type),
|
|
info.address,
|
|
info.pc,
|
|
info.ra);
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
y += 180;
|
|
} else if (info.type == MIPSExceptionType::FPU) {
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
FPU: %s
|
|
PC: %08x
|
|
)", FPUExceptionTypeAsString(info.fpu_type), info.pc);
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
y += 180;
|
|
} else if (info.type == MIPSExceptionType::BREAK) {
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
BREAK
|
|
PC: %08x
|
|
)", info.pc);
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
y += 180;
|
|
} else {
|
|
snprintf(statbuf, sizeof(statbuf), R"(
|
|
Invalid / Unknown (%d)
|
|
)", (int)info.type);
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
y += 180;
|
|
}
|
|
|
|
std::string kernelState = __KernelStateSummary();
|
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, kernelState.c_str(), x, y, 0xFFFFFFFF);
|
|
|
|
y += 40;
|
|
|
|
ctx->Draw()->SetFontScale(.5f, .5f);
|
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, info.stackTrace.c_str(), x, y, 0xFFFFFFFF);
|
|
|
|
ctx->Draw()->SetFontScale(.7f, .7f);
|
|
|
|
ctx->PopScissor();
|
|
|
|
// Draw some additional stuff to the right, explaining why the background is purple, if it is.
|
|
// Should try to be in sync with Reporting::IsSupported().
|
|
|
|
std::string tips;
|
|
if (CheatsInEffect()) {
|
|
tips += "* Turn off cheats.\n";
|
|
}
|
|
if (HLEPlugins::HasEnabled()) {
|
|
tips += "* Turn off plugins.\n";
|
|
}
|
|
if (g_Config.uJitDisableFlags) {
|
|
tips += StringFromFormat("* Don't use JitDisableFlags: %08x\n", g_Config.uJitDisableFlags);
|
|
}
|
|
if (GetLockedCPUSpeedMhz()) {
|
|
tips += "* Set CPU clock to default (0)\n";
|
|
}
|
|
if (checkingISO) {
|
|
tips += "* (waiting for CRC...)\n";
|
|
} else if (!isoOK) {
|
|
tips += "* Verify and possibly re-dump your ISO\n (CRC not recognized)\n";
|
|
}
|
|
if (g_paramSFO.GetValueString("DISC_VERSION").empty()) {
|
|
tips += "\n(DISC_VERSION is empty)\n";
|
|
}
|
|
if (!tips.empty()) {
|
|
tips = "Things to try:\n" + tips;
|
|
}
|
|
|
|
x += columnWidth + 10;
|
|
y = 85;
|
|
snprintf(statbuf, sizeof(statbuf),
|
|
"CPU Core: %s (flags: %08x)\n"
|
|
"Locked CPU freq: %d MHz\n"
|
|
"Cheats: %s, Plugins: %s\n%s\n\n%s",
|
|
CPUCoreAsString(g_Config.iCpuCore), g_Config.uJitDisableFlags,
|
|
GetLockedCPUSpeedMhz(),
|
|
CheatsInEffect() ? "Y" : "N", HLEPlugins::HasEnabled() ? "Y" : "N", activeFlags.c_str(), tips.c_str());
|
|
|
|
ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF);
|
|
ctx->Flush();
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
ctx->RebindTexture();
|
|
}
|
|
|
|
void DrawFPS(UIContext *ctx, const Bounds &bounds) {
|
|
FontID ubuntu24("UBUNTU24");
|
|
float vps, fps, actual_fps;
|
|
__DisplayGetFPS(&vps, &fps, &actual_fps);
|
|
|
|
char temp[256];
|
|
StringWriter w(temp);
|
|
|
|
if ((g_Config.iShowStatusFlags & ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) == ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) {
|
|
// Both at the same time gets a shorter formulation.
|
|
w.F("%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / ((g_Config.iDisplayRefreshRate / 60.0f * 59.94f) / 100.0f));
|
|
} else {
|
|
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::FPS_COUNTER) {
|
|
w.F("FPS: %0.1f", actual_fps);
|
|
} else if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::SPEED_COUNTER) {
|
|
w.F("Speed: %0.1f%%", vps / (59.94f / 100.0f));
|
|
}
|
|
}
|
|
if (System_GetPropertyBool(SYSPROP_CAN_READ_BATTERY_PERCENTAGE)) {
|
|
if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::BATTERY_PERCENT) {
|
|
const int percentage = System_GetPropertyInt(SYSPROP_BATTERY_PERCENTAGE);
|
|
// Just plain append battery. Add linebreak?
|
|
w.F(" Battery: %d%%", percentage);
|
|
}
|
|
}
|
|
|
|
ctx->Flush();
|
|
|
|
float offset = 10.0f;
|
|
#ifdef MOBILE_DEVICE
|
|
offset += 10.0f;
|
|
#endif
|
|
|
|
ctx->BindFontTexture();
|
|
ctx->Draw()->SetFontScale(0.7f, 0.7f);
|
|
ctx->Draw()->DrawText(ubuntu24, w.as_view(), bounds.x2() - offset, bounds.y + 12, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->DrawText(ubuntu24, w.as_view(), bounds.x2() - offset - 2, bounds.y + 10, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
|
|
ctx->Draw()->SetFontScale(1.0f, 1.0f);
|
|
ctx->Flush();
|
|
ctx->RebindTexture();
|
|
}
|