diff --git a/CMakeLists.txt b/CMakeLists.txt index ec22f64164..8c32e17af1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -715,6 +715,7 @@ add_library(Common STATIC Common/Data/Text/I18n.h Common/Data/Text/Parsers.cpp Common/Data/Text/Parsers.h + Common/Data/Text/StringWriter.h Common/Data/Text/WrapText.cpp Common/Data/Text/WrapText.h Common/Data/Random/Rng.h diff --git a/Common/Buffer.h b/Common/Buffer.h index 892f496e7f..f9c3a474ec 100644 --- a/Common/Buffer.h +++ b/Common/Buffer.h @@ -57,7 +57,7 @@ public: int SkipLineCRLF(); // Utility functions. - void Printf(const char *fmt, ...); + ATTR_FORMAT_PRINTF(2, 3) void Printf(MSVC_FORMAT_PRINTF const char *fmt, ...); // Dumps the entire buffer to the string, but keeps it around. // Only to be used for debugging, since it might not be fast at all. diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 86bdff49a9..41021a051c 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -411,6 +411,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 34c66be029..048031eb65 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -781,6 +781,9 @@ ext\pugixml + + Data\Text + diff --git a/Common/CommonFuncs.h b/Common/CommonFuncs.h index d3916a8ad8..b9153ba0f3 100644 --- a/Common/CommonFuncs.h +++ b/Common/CommonFuncs.h @@ -132,3 +132,19 @@ inline u64 __rotr64(u64 x, unsigned int shift){ return (x >> n) | (x << (64 - n)); #endif } + +// --- GCC / Clang / Intel Definition --- +#if defined(__GNUC__) || defined(__clang__) +#define ATTR_FORMAT_PRINTF(fmt_idx, first_arg_idx) \ + __attribute__((format(printf, fmt_idx, first_arg_idx))) +#else +#define ATTR_FORMAT_PRINTF(fmt_idx, first_arg_idx) +#endif + +// --- MSVC Definition --- +#if defined(_MSC_VER) +#include +#define MSVC_FORMAT_PRINTF _Printf_format_string_ +#else +#define MSVC_FORMAT_PRINTF +#endif diff --git a/Common/Data/Text/Parsers.cpp b/Common/Data/Text/Parsers.cpp index 4e2789b82c..b5b5c0992c 100644 --- a/Common/Data/Text/Parsers.cpp +++ b/Common/Data/Text/Parsers.cpp @@ -6,6 +6,7 @@ #include "Common/Data/Text/Parsers.h" #include "Common/Data/Text/I18n.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/StringUtils.h" // Not strictly a parser... diff --git a/Common/Data/Text/Parsers.h b/Common/Data/Text/Parsers.h index defe17ff40..f743ac9c7b 100644 --- a/Common/Data/Text/Parsers.h +++ b/Common/Data/Text/Parsers.h @@ -79,74 +79,3 @@ std::string NiceSizeFormat(uint64_t size); // seconds, or minutes, or hours. // Uses I18N strings. std::string NiceTimeFormat(int seconds); - -// Not a parser, needs a better location. -// Simplified version of ShaderWriter. Would like to have that inherit from this but can't figure out how -// due to the return value chaining. -// TODO: We actually also have Buffer, with .Printf(), which is almost as convenient. Should maybe improve that instead? -class StringWriter { -public: - StringWriter(char *buffer, size_t bufSize) : start_(buffer), p_(buffer), bufSize_(bufSize) { - buffer[0] = '\0'; - } - template - explicit StringWriter(char (&buffer)[sz]) : start_(buffer), p_(buffer), bufSize_(sz) { - buffer[0] = '\0'; - } - StringWriter(const StringWriter &) = delete; - - std::string_view as_view() const { - return std::string_view(start_, p_ - start_); - } - - size_t size() const { - return p_ - start_; - } - - // Assumes the input is zero-terminated. - // C: Copies a string literal (which always are zero-terminated, the count includes the zero) directly to the stream. - template - StringWriter &C(const char(&text)[T]) { - const size_t remainder = bufSize_ - (p_ - start_); - if (remainder <= T) - return *this; - memcpy(p_, text, T); - p_ += T - 1; - return *this; - } - // Assumes the input is zero-terminated. - // C: Copies a string literal (which always are zero-terminated, the count includes the zero) directly to the stream. - StringWriter &B(bool b) { - return W(b ? "true" : "false"); - } - - // W: Writes a string_view to the stream. - StringWriter &W(std::string_view text) { - const size_t remainder = bufSize_ - (p_ - start_); - if (remainder <= text.length()) - return *this; - memcpy(p_, text.data(), text.length()); - p_ += text.length(); - *p_ = '\0'; - return *this; - } - - // F: Formats into the buffer. - StringWriter &F(const char *format, ...); - StringWriter &endl() { - const size_t remainder = bufSize_ - (p_ - start_); - if (remainder <= 2) - return *this; - - return C("\n"); - } - - void Rewind(size_t offset) { - p_ -= offset; - } - -private: - char *start_; - char *p_; - size_t bufSize_; -}; diff --git a/Common/Data/Text/StringWriter.cpp b/Common/Data/Text/StringWriter.cpp new file mode 100644 index 0000000000..8d8ba6f53a --- /dev/null +++ b/Common/Data/Text/StringWriter.cpp @@ -0,0 +1,2 @@ +#include "Common/Data/Text/StringWriter.h" + diff --git a/Common/Data/Text/StringWriter.h b/Common/Data/Text/StringWriter.h new file mode 100644 index 0000000000..1dfa50f181 --- /dev/null +++ b/Common/Data/Text/StringWriter.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include // for memcpy + +#include "Common/CommonFuncs.h" + +// Not a parser, needs a better location. +// Simplified version of ShaderWriter. Would like to have that inherit from this but can't figure out how +// due to the return value chaining. Maybe with the curiously recurring template pattern? +// TODO: We actually also have Buffer, with .Printf(), which is almost as convenient. Should maybe improve that instead? +class StringWriter { +public: + StringWriter(char *buffer, size_t bufSize) : start_(buffer), p_(buffer), bufSize_(bufSize) { + buffer[0] = '\0'; + } + template + explicit StringWriter(char(&buffer)[sz]) : start_(buffer), p_(buffer), bufSize_(sz) { + buffer[0] = '\0'; + } + StringWriter(const StringWriter &) = delete; + + std::string_view as_view() const { + return std::string_view(start_, p_ - start_); + } + + size_t size() const { + return p_ - start_; + } + + // Assumes the input is zero-terminated. + // C: Copies a string literal (which always are zero-terminated, the count includes the zero) directly to the stream. + template + StringWriter &C(const char(&text)[T]) { + const size_t remainder = bufSize_ - (p_ - start_); + if (remainder <= T) + return *this; + memcpy(p_, text, T); + p_ += T - 1; + return *this; + } + // Assumes the input is zero-terminated. + // C: Copies a string literal (which always are zero-terminated, the count includes the zero) directly to the stream. + StringWriter &B(bool b) { + return W(b ? "true" : "false"); + } + + // W: Writes a string_view to the stream. + StringWriter &W(std::string_view text) { + const size_t remainder = bufSize_ - (p_ - start_); + if (remainder <= text.length()) + return *this; + memcpy(p_, text.data(), text.length()); + p_ += text.length(); + *p_ = '\0'; + return *this; + } + + // F: Formats into the buffer. + // This one is implemented in Parsers.cpp. + StringWriter &F(MSVC_FORMAT_PRINTF const char *format, ...) ATTR_FORMAT_PRINTF(2, 3); + StringWriter &endl() { + const size_t remainder = bufSize_ - (p_ - start_); + if (remainder <= 2) + return *this; + + return C("\n"); + } + + void Rewind(size_t offset) { + p_ -= offset; + } + +private: + char *start_; + char *p_; + size_t bufSize_; +}; diff --git a/Common/GPU/OpenGL/GLProfiler.h b/Common/GPU/OpenGL/GLProfiler.h index 3290134547..c585cd2c08 100644 --- a/Common/GPU/OpenGL/GLProfiler.h +++ b/Common/GPU/OpenGL/GLProfiler.h @@ -4,6 +4,7 @@ #include #include +#include "Common/CommonFuncs.h" #include "Common/GPU/OpenGL/GLCommon.h" #include "Common/GPU/OpenGL/gl3stub.h" @@ -25,11 +26,8 @@ public: void BeginFrame(); - void Begin(const char *fmt, ...) -#ifdef __GNUC__ - __attribute__((format(printf, 2, 3))) -#endif - ; + ATTR_FORMAT_PRINTF(2, 3) + void Begin(MSVC_FORMAT_PRINTF const char *fmt, ...); void End(); void SetEnabledPtr(bool *enabledPtr) { diff --git a/Common/GPU/ShaderWriter.h b/Common/GPU/ShaderWriter.h index bb686eda35..7ffcaaef99 100644 --- a/Common/GPU/ShaderWriter.h +++ b/Common/GPU/ShaderWriter.h @@ -35,6 +35,7 @@ enum class ShaderWriterFlags { }; ENUM_CLASS_BITOPS(ShaderWriterFlags); +// TODO: Somehow merge with StringWriter from Parsers.h class ShaderWriter { public: // Extensions are supported for both OpenGL ES and Vulkan (though of course, they're different). diff --git a/Common/GPU/Vulkan/VulkanProfiler.h b/Common/GPU/Vulkan/VulkanProfiler.h index 8d33d63ca4..1c5cfd8c42 100644 --- a/Common/GPU/Vulkan/VulkanProfiler.h +++ b/Common/GPU/Vulkan/VulkanProfiler.h @@ -3,6 +3,7 @@ #include #include +#include "Common/CommonFuncs.h" #include "VulkanLoader.h" // Simple scoped based profiler, initially meant for instant one-time tasks like texture uploads @@ -29,11 +30,7 @@ public: void BeginFrame(VulkanContext *vulkan, VkCommandBuffer firstCommandBuffer); - void Begin(VkCommandBuffer cmdBuf, VkPipelineStageFlagBits stage, const char *fmt, ...) -#ifdef __GNUC__ - __attribute__((format(printf, 4, 5))) -#endif - ; + ATTR_FORMAT_PRINTF(4, 5) void Begin(VkCommandBuffer cmdBuf, VkPipelineStageFlagBits stage, MSVC_FORMAT_PRINTF const char *fmt, ...); void End(VkCommandBuffer cmdBuf, VkPipelineStageFlagBits stage); void SetEnabledPtr(bool *enabledPtr) { diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 9458df4137..f99673d5cb 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -896,7 +896,7 @@ public: // Not very elegant, but more elegant than the old passId hack. virtual void SetInvalidationCallback(InvalidationCallback callback) = 0; - // Total amount of frames rendered. Unaffected by game pause, so more robust than gpuStats.numFlips + // Total amount of frames rendered. Unaffected by game pause, so more robust than gpuStats.totals.numFlips virtual int GetFrameCount() = 0; virtual std::string GetGpuProfileString() const { diff --git a/Common/Log.h b/Common/Log.h index 268bfc35fb..c9845dfe93 100644 --- a/Common/Log.h +++ b/Common/Log.h @@ -109,11 +109,7 @@ inline bool GenericLogEnabled(Log type, LogLevel level) { return g_log[(int)type].IsEnabled(level) && (*g_bLogEnabledSetting); } -void GenericLog(Log type, LogLevel level, const char *file, int line, const char *fmt, ...) -#ifdef __GNUC__ - __attribute__((format(printf, 5, 6))) -#endif - ; +ATTR_FORMAT_PRINTF(5, 6) void GenericLog(Log type, LogLevel level, const char *file, int line, MSVC_FORMAT_PRINTF const char *fmt, ...); // If you want to see verbose logs, change this to VERBOSE_LEVEL. @@ -134,11 +130,7 @@ void GenericLog(Log type, LogLevel level, const char *file, int line, const char #define VERBOSE_LOG(t,...) do { GENERIC_LOG(t, LogLevel::LVERBOSE, __VA_ARGS__) } while (false) // Currently only actually shows a dialog box on Windows. -bool HandleAssert(bool isDebugAssert, const char *function, const char *file, int line, const char *expression, const char* format, ...) -#ifdef __GNUC__ -__attribute__((format(printf, 6, 7))) -#endif -; +ATTR_FORMAT_PRINTF(6, 7) bool HandleAssert(bool isDebugAssert, const char *function, const char *file, int line, const char *expression, MSVC_FORMAT_PRINTF const char *format, ...); // These allow us to get a small amount of information into assert messages. // They can have a value between 0 and 15. diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 8b0e4759d9..4553d50bbd 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -3,6 +3,7 @@ #include "Common/Math/math_util.h" #include "Common/TimeUtil.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/StringUtils.h" #include "Common/Log.h" @@ -765,21 +766,20 @@ void ControlMapper::onVKey(VirtKey vkey, bool down) { } } -void ControlMapper::GetDebugString(char *buffer, size_t bufSize) const { - std::stringstream str; +void ControlMapper::GetDebugString(StringWriter &w) const { for (auto &iter : curInput_) { char temp[256]; iter.first.FormatDebug(temp, sizeof(temp)); - str << temp << ": " << iter.second.value << std::endl; + w.F("%s: %f\n", temp, iter.second.value); } for (int i = 0; i < ARRAY_SIZE(virtKeys_); i++) { int vkId = VIRTKEY_FIRST + i; if ((vkId >= VIRTKEY_AXIS_X_MIN && vkId <= VIRTKEY_AXIS_Y_MAX) || vkId == VIRTKEY_ANALOG_LIGHTLY || vkId == VIRTKEY_SPEED_ANALOG) { - str << KeyMap::GetPspButtonName(vkId) << ": " << virtKeys_[i] << std::endl; + w.F("%s: %f\n", KeyMap::GetPspButtonName(vkId).c_str(), virtKeys_[i]); } } - str << "Lstick: " << converted_[0][0] << ", " << converted_[0][1] << std::endl; - truncate_cpy(buffer, bufSize, str.str().c_str()); + w.F("Lstick: %f, %f\n", converted_[0][0], converted_[0][1]); + w.F("Rstick: %f, %f\n", converted_[1][0], converted_[1][1]); } void ControlMapper::RemoveListener(ControlListener *listener) { diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 9f496eeaed..5ededf210c 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -21,6 +21,8 @@ public: virtual void SetRawAnalog(int stick, float x, float y) {} }; +class StringWriter; + // Utilities for mapping input events to PSP inputs and virtual keys. // Main use is of course from EmuScreen.cpp, but also useful from control settings etc. class ControlMapper { @@ -56,7 +58,7 @@ public: // Call when the emu screen gets pushed behind some other screen, like the pause screen, to release all "down" inputs. void ReleaseAll(); - void GetDebugString(char *buffer, size_t bufSize) const; + void GetDebugString(StringWriter &w) const; struct InputSample { float value; diff --git a/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp b/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp index 6c17842c81..4da650889f 100644 --- a/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp +++ b/Core/Debugger/WebSocket/GPUStatsSubscriber.cpp @@ -17,6 +17,8 @@ #include #include + +#include "Common/Data/Text/StringWriter.h" #include "Core/Debugger/WebSocket/GPUStatsSubscriber.h" #include "Core/HW/Display.h" #include "Core/System.h" @@ -120,7 +122,9 @@ void WebSocketGPUStatsState::FlipListener() { CollectedStats &stats = pendingStats_[pendingStats_.size() - 1]; __DisplayGetFPS(&stats.vps, &stats.fps, &stats.actual_fps); - __DisplayGetDebugStats(stats.statbuf, sizeof(stats.statbuf)); + + StringWriter w(stats.statbuf); + __DisplayGetDebugStats(w); int valid; float *sleepHistory; diff --git a/Core/HLE/HLE.h b/Core/HLE/HLE.h index 3c18ff2e47..10d1af1ec8 100644 --- a/Core/HLE/HLE.h +++ b/Core/HLE/HLE.h @@ -190,11 +190,8 @@ void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line template [[nodiscard]] -#ifdef __GNUC__ -__attribute__((format(printf, 7, 8))) -#endif NO_INLINE -T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char *reportTag, const char *reasonFmt, ...) { +ATTR_FORMAT_PRINTF(7, 8) T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char *reportTag, MSVC_FORMAT_PRINTF const char *reasonFmt, ...) { if (!GenericLogEnabled(t, level)) { if (leave) { hleLeave(); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 9cc9bc7af6..ea75c803bc 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -28,6 +28,7 @@ #endif #include "Common/Data/Text/I18n.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/Profiler/Profiler.h" #include "Common/System/System.h" #include "Common/System/OSD.h" @@ -273,7 +274,7 @@ void __DisplayDoState(PointerWrap &p) { gstate_c.DoState(p); if (s < 2) { // This shouldn't have been savestated anyway, but it was. - // It's unlikely to overlap with the first value in gpuStats. + // It's unlikely to overlap with the first value in gpuStats.perFrame. int gpuVendorTemp = 0; p.ExpectVoid(&gpuVendorTemp, sizeof(gpuVendorTemp)); } @@ -395,7 +396,8 @@ static void DoFrameDropLogging(float scaledTimestep) { const double actualTimestep = curFrameTime - lastFrameTime; char stats[4096]; - __DisplayGetDebugStats(stats, sizeof(stats)); + StringWriter w(stats); + __DisplayGetDebugStats(w); NOTICE_LOG(Log::sceDisplay, "Dropping frames - budget = %.2fms / %.1ffps, actual = %.2fms (+%.2fms) / %.1ffps\n%s", scaledTimestep * 1000.0, 1.0 / scaledTimestep, actualTimestep * 1000.0, (actualTimestep - scaledTimestep) * 1000.0, 1.0 / actualTimestep, stats); } } @@ -674,7 +676,7 @@ void __DisplayFlip(int cyclesLate) { } if (fbDirty) { - gpuStats.numFlips++; + gpuStats.totals.numFlips++; } float scaledTimestep = (float)numVBlanksSinceFlip * timePerVblank; diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index b6423ee395..7f36b7e1bf 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -21,7 +21,6 @@ #include "zlib.h" #include "Common/Data/Convert/SmallDataConvert.h" -#include "Common/Data/Text/Parsers.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeSet.h" @@ -31,6 +30,7 @@ #include "Common/System/System.h" #include "Common/System/OSD.h" #include "Common/Data/Text/I18n.h" +#include "Common/Data/Text/StringWriter.h" #include "Core/Config.h" #include "Core/Core.h" @@ -363,7 +363,7 @@ void PSPModule::GetLongInfo(char *ptr, int bufSize) const { } w.F("Text: %08x (%08x bytes)\n", nm.text_addr, nm.text_size); w.F("Data: %08x (%08x bytes)\n", GetDataAddr(), nm.data_size); - w.F("BSS: % 08x(% 08x bytes)\n", GetBSSAddr(), nm.bss_size); + w.F("BSS: %08x (%08x bytes)\n", GetBSSAddr(), nm.bss_size); w.F("Entry: %08x GP: %08x\n", nm.entry_addr, nm.gp_value); w.F("Status: %08x\n", nm.status); } diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 592f31db6b..ed17563880 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -22,6 +22,7 @@ #include "Common/Net/Resolve.h" #include "Common/Net/SocketCompat.h" #include "Common/Data/Text/Parsers.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/Data/Text/I18n.h" #include "Common/File/VFS/VFS.h" #include "Common/File/FileUtil.h" diff --git a/Core/HW/Display.cpp b/Core/HW/Display.cpp index 1ac986aa1d..162718b237 100644 --- a/Core/HW/Display.cpp +++ b/Core/HW/Display.cpp @@ -23,6 +23,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Common/System/System.h" #include "Common/TimeUtil.h" +#include "Common/Data/Text/StringWriter.h" #include "Core/Config.h" #include "Core/System.h" #include "Core/CoreTiming.h" @@ -79,10 +80,10 @@ static void CalculateFPS() { actualFps = (float)(actualFlips - lastActualFlips); fps = frames / (now - lastFpsTime); - flips = (float)(g_Config.iDisplayRefreshRate * (double)(gpuStats.numFlips - lastNumFlips) / frames); + flips = (float)(g_Config.iDisplayRefreshRate * (double)(gpuStats.totals.numFlips - lastNumFlips) / frames); lastFpsFrame = numVBlanks; - lastNumFlips = gpuStats.numFlips; + lastNumFlips = gpuStats.totals.numFlips; lastActualFlips = actualFlips; lastFpsTime = now; @@ -187,24 +188,12 @@ void DisplayNotifySleep(double t, int pos) { frameSleepHistory[pos] += t; } -void __DisplayGetDebugStats(char *stats, size_t bufsize) { - char statbuf[4096]; +void __DisplayGetDebugStats(StringWriter &w) { if (!gpu) { - snprintf(stats, bufsize, "N/A"); + w.C("No GPU stats available").endl(); return; } - gpu->GetStats(statbuf, sizeof(statbuf)); - - snprintf(stats, bufsize, - "Kernel processing time: %0.2f ms\n" - "Slowest syscall: %s : %0.2f ms\n" - "Most active syscall: %s : %0.2f ms\n%s", - kernelStats.msInSyscalls * 1000.0f, - kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", - kernelStats.slowestSyscallTime * 1000.0f, - kernelStats.summedSlowestSyscallName ? kernelStats.summedSlowestSyscallName : "(none)", - kernelStats.summedSlowestSyscallTime * 1000.0f, - statbuf); + gpu->GetStats(w); } // On like 90hz, 144hz, etc, we return 60.0f as the framerate target. We only target other diff --git a/Core/HW/Display.h b/Core/HW/Display.h index 53a46dee06..75c6278942 100644 --- a/Core/HW/Display.h +++ b/Core/HW/Display.h @@ -21,6 +21,7 @@ #include class PointerWrap; +class StringWriter; typedef void (*FlipCallback)(void *userdata); void __DisplayListenFlip(FlipCallback callback, void *userdata); @@ -35,7 +36,7 @@ uint32_t __DisplayGetCurrentHcount(); uint32_t __DisplayGetAccumulatedHcount(); void DisplayAdjustAccumulatedHcount(uint32_t diff); -void __DisplayGetDebugStats(char *stats, size_t bufsize); +void __DisplayGetDebugStats(StringWriter &w); void __DisplayGetAveragedFPS(float *out_vps, float *out_fps); void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps); void __DisplayGetVPS(float *out_vps); diff --git a/Core/Util/PPGeDraw.cpp b/Core/Util/PPGeDraw.cpp index 8effbb102f..e3d28df4a8 100644 --- a/Core/Util/PPGeDraw.cpp +++ b/Core/Util/PPGeDraw.cpp @@ -911,7 +911,7 @@ static PPGeTextDrawerImage PPGeGetTextImage(std::string_view text, const PPGeSty auto cacheItem = textDrawerImages.find(key); if (cacheItem != textDrawerImages.end()) { im = cacheItem->second; - cacheItem->second.entry.lastUsedFrame = gpuStats.numFlips; + cacheItem->second.entry.lastUsedFrame = gpuStats.totals.numFlips; } else { std::vector bitmapData; textDrawer->SetFontScale(style.scale, style.scale); @@ -944,7 +944,7 @@ static PPGeTextDrawerImage PPGeGetTextImage(std::string_view text, const PPGeSty memset(ramPtr + im.entry.bmHeight * bufwBytes, 0, bufwBytes + sz - origSz); } - im.entry.lastUsedFrame = gpuStats.numFlips; + im.entry.lastUsedFrame = gpuStats.totals.numFlips; textDrawerImages[key] = im; } @@ -1049,7 +1049,7 @@ static void PPGeDrawTextImage(const PPGeTextDrawerImage &im, float x, float y, c static void PPGeDecimateTextImages(int age) { // Do this always, in case the platform has no TextDrawer but save state did. for (auto it = textDrawerImages.begin(); it != textDrawerImages.end(); ) { - if (gpuStats.numFlips - it->second.entry.lastUsedFrame >= age) { + if (gpuStats.totals.numFlips - it->second.entry.lastUsedFrame >= age) { kernelMemory.Free(it->second.ptr); it = textDrawerImages.erase(it); } else { @@ -1409,7 +1409,7 @@ bool PPGeImage::Load() { Memory::Memset(texture_ + dataSize, 0, texSize - dataSize, "PPGeTexClear"); free(textureData); - lastFrame_ = gpuStats.numFlips; + lastFrame_ = gpuStats.totals.numFlips; loadedTextures_.push_back(this); return true; } @@ -1467,7 +1467,7 @@ void PPGeImage::CompatLoad(u32 texture, int width, int height) { } void PPGeImage::Decimate(int age) { - int tooOldFrame = gpuStats.numFlips - age; + int tooOldFrame = gpuStats.totals.numFlips - age; for (size_t i = 0; i < loadedTextures_.size(); ++i) { if (loadedTextures_[i]->lastFrame_ < tooOldFrame) { loadedTextures_[i]->Free(); @@ -1484,7 +1484,7 @@ void PPGeImage::SetTexture() { } if (texture_ != 0) { - lastFrame_ = gpuStats.numFlips; + lastFrame_ = gpuStats.totals.numFlips; PPGeSetTexture(texture_, width_, height_); } else { PPGeDisableTexture(); diff --git a/GPU/Common/DepthRaster.cpp b/GPU/Common/DepthRaster.cpp index e467af4f7e..273950df71 100644 --- a/GPU/Common/DepthRaster.cpp +++ b/GPU/Common/DepthRaster.cpp @@ -518,7 +518,7 @@ int DepthRasterClipIndexedTriangles(int *tx, int *ty, float *tz, const float *tr // Still good for backface culling early and pretty cheap to compute. Vec4F32 doubleTriArea = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0) - Vec4F32::Splat((float)(MIN_TWICE_TRI_AREA)); if (!AnyZeroSignBit(doubleTriArea)) { - gpuStats.numDepthRasterEarlySize += 4; + gpuStats.perFrame.numDepthRasterEarlySize += 4; continue; } @@ -560,8 +560,8 @@ int DepthRasterClipIndexedTriangles(int *tx, int *ty, float *tz, const float *tr } } - gpuStats.numDepthRasterZCulled += planeCulled; - gpuStats.numDepthEarlyBoxCulled += boxCulled; + gpuStats.perFrame.numDepthRasterZCulled += planeCulled; + gpuStats.perFrame.numDepthEarlyBoxCulled += boxCulled; return outCount; } @@ -578,7 +578,7 @@ void DepthRasterScreenVerts(uint16_t *depth, int depthStride, const int *tx, con // We remove the subpixel information here. DepthRasterRect(depth, depthStride, scissor, tx[i], ty[i], tx[i + 1], ty[i + 1], z, draw.compareMode); } - gpuStats.numDepthRasterPrims += count / 2; + gpuStats.perFrame.numDepthRasterPrims += count / 2; break; case GE_PRIM_TRIANGLES: { @@ -633,9 +633,9 @@ void DepthRasterScreenVerts(uint16_t *depth, int depthStride, const int *tx, con } } } - gpuStats.numDepthRasterNoPixels += stats[(int)TriangleStat::NoPixels]; - gpuStats.numDepthRasterTooSmall += stats[(int)TriangleStat::SmallOrBackface]; - gpuStats.numDepthRasterPrims += stats[(int)TriangleStat::OK]; + gpuStats.perFrame.numDepthRasterNoPixels += stats[(int)TriangleStat::NoPixels]; + gpuStats.perFrame.numDepthRasterTooSmall += stats[(int)TriangleStat::SmallOrBackface]; + gpuStats.perFrame.numDepthRasterPrims += stats[(int)TriangleStat::OK]; break; } default: diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index bada491ea0..36613e220c 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -571,7 +571,7 @@ void DrawEngineCommon::ApplyFramebufferRead(FBOTexState *fboTexState) { if (gstate_c.Use(GPU_USE_FRAMEBUFFER_FETCH)) { *fboTexState = FBO_TEX_READ_FRAMEBUFFER; } else { - gpuStats.numCopiesForShaderBlend++; + gpuStats.perFrame.numCopiesForShaderBlend++; *fboTexState = FBO_TEX_COPY_BIND_TEX; } gstate_c.Dirty(DIRTY_SHADERBLEND); @@ -1037,7 +1037,7 @@ void DrawEngineCommon::DepthRasterSubmitRaw(GEPrimitiveType prim, const VertexDe return; } - TimeCollector collectStat(&gpuStats.msPrepareDepth, coreCollectDebugStats); + TimeCollector collectStat(&gpuStats.perFrame.msPrepareDepth, coreCollectDebugStats); // Decode. int numDecoded = 0; @@ -1079,7 +1079,7 @@ void DrawEngineCommon::DepthRasterPredecoded(GEPrimitiveType prim, const void *i return; } - TimeCollector collectStat(&gpuStats.msPrepareDepth, coreCollectDebugStats); + TimeCollector collectStat(&gpuStats.perFrame.msPrepareDepth, coreCollectDebugStats); // Make sure these have already been indexed away. _dbg_assert_(prim != GE_PRIM_TRIANGLE_STRIP && prim != GE_PRIM_TRIANGLE_FAN); @@ -1112,7 +1112,7 @@ void DrawEngineCommon::DepthRasterPredecoded(GEPrimitiveType prim, const void *i void DrawEngineCommon::FlushQueuedDepth() { if (rasterTimeStart_ != 0.0) { - gpuStats.msRasterTimeAvailable += time_now_d() - rasterTimeStart_; + gpuStats.perFrame.msRasterTimeAvailable += time_now_d() - rasterTimeStart_; rasterTimeStart_ = 0.0; } @@ -1131,7 +1131,7 @@ void DrawEngineCommon::FlushQueuedDepth() { DepthScissor tileScissor = draw.scissor.Tile(0, 1); { - TimeCollector collectStat(&gpuStats.msCullDepth, collectStats); + TimeCollector collectStat(&gpuStats.perFrame.msCullDepth, collectStats); switch (draw.prim) { case GE_PRIM_RECTANGLES: outVertCount = DepthRasterClipIndexedRectangles(tx, ty, tz, vertices, indices, draw, tileScissor); @@ -1145,7 +1145,7 @@ void DrawEngineCommon::FlushQueuedDepth() { } } if (outVertCount > 0) { - TimeCollector collectStat(&gpuStats.msRasterizeDepth, collectStats); + TimeCollector collectStat(&gpuStats.perFrame.msRasterizeDepth, collectStats); if (!Memory::IsValid4AlignedAddress(draw.depthAddr)) { continue; } diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 76344e82ed..5f32f00d8a 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -213,11 +213,11 @@ protected: } inline void ResetAfterDrawInline() { - gpuStats.numFlushes++; - gpuStats.numDrawCalls += numDrawInds_; - gpuStats.numVertexDecodes += numDrawVerts_; - gpuStats.numVertsSubmitted += vertexCountInDrawCalls_; - gpuStats.numVertsDecoded += numDecodedVerts_; + gpuStats.perFrame.numFlushes++; + gpuStats.perFrame.numDrawCalls += numDrawInds_; + gpuStats.perFrame.numVertexDecodes += numDrawVerts_; + gpuStats.perFrame.numVertsSubmitted += vertexCountInDrawCalls_; + gpuStats.perFrame.numVertsDecoded += numDecodedVerts_; indexGen.Reset(); numDecodedVerts_ = 0; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 3bad4401b1..09644bc3c7 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -479,8 +479,8 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer // If it's newly wrong, or changing every frame, just keep track. vfb->newWidth = drawing_width; vfb->newHeight = drawing_height; - vfb->lastFrameNewSize = gpuStats.numFlips; - } else if (vfb->lastFrameNewSize + FBO_OLD_AGE < gpuStats.numFlips) { + vfb->lastFrameNewSize = gpuStats.totals.numFlips; + } else if (vfb->lastFrameNewSize + FBO_OLD_AGE < gpuStats.totals.numFlips) { // Okay, it's changed for a while (and stayed that way.) Let's start over. // But only if we really need to, to avoid blinking. bool needsRecreate = vfb->bufferWidth > params.fb_stride; @@ -502,7 +502,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer } } else { // It's not different, let's keep track of that too. - vfb->lastFrameNewSize = gpuStats.numFlips; + vfb->lastFrameNewSize = gpuStats.totals.numFlips; } if (!resized && renderScaleFactor_ != 1 && vfb->renderScaleFactor == 1) { @@ -530,7 +530,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer vfb->height = drawing_height; vfb->newWidth = drawing_width; vfb->newHeight = drawing_height; - vfb->lastFrameNewSize = gpuStats.numFlips; + vfb->lastFrameNewSize = gpuStats.totals.numFlips; vfb->fb_format = params.fb_format; vfb->usageFlags = FB_USAGE_RENDER_COLOR; @@ -557,8 +557,8 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer INFO_LOG(Log::FrameBuf, "Creating FBO for %08x (z: %08x) : %d x %d x %s", vfb->fb_address, vfb->z_address, vfb->width, vfb->height, GeBufferFormatToString(vfb->fb_format)); - vfb->last_frame_render = gpuStats.numFlips; - frameLastFramebufUsed_ = gpuStats.numFlips; + vfb->last_frame_render = gpuStats.totals.numFlips; + frameLastFramebufUsed_ = gpuStats.totals.numFlips; vfbs_.push_back(vfb); currentRenderVfb_ = vfb; @@ -577,8 +577,8 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer // Use it as a render target. DEBUG_LOG(Log::FrameBuf, "Switching render target to FBO for %08x: %d x %d x %d ", vfb->fb_address, vfb->width, vfb->height, vfb->fb_format); vfb->usageFlags |= FB_USAGE_RENDER_COLOR; - vfb->last_frame_render = gpuStats.numFlips; - frameLastFramebufUsed_ = gpuStats.numFlips; + vfb->last_frame_render = gpuStats.totals.numFlips; + frameLastFramebufUsed_ = gpuStats.totals.numFlips; vfb->dirtyAfterDisplay = true; if ((skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) vfb->reallyDirtyAfterDisplay = true; @@ -593,8 +593,8 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(Framebuffer } else { // Something changed, but we still got the same framebuffer we were already rendering to. // Might not be a lot to do here, we check in NotifyRenderFramebufferUpdated - vfb->last_frame_render = gpuStats.numFlips; - frameLastFramebufUsed_ = gpuStats.numFlips; + vfb->last_frame_render = gpuStats.totals.numFlips; + frameLastFramebufUsed_ = gpuStats.totals.numFlips; vfb->dirtyAfterDisplay = true; if ((skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) vfb->reallyDirtyAfterDisplay = true; @@ -715,8 +715,8 @@ void FramebufferManagerCommon::CopyToDepthFromOverlappingFramebuffers(VirtualFra if (source.channel == RASTER_DEPTH) { // Good old depth->depth copy. BlitFramebufferDepth(source.vfb, dest); - gpuStats.numDepthCopies++; - dest->last_frame_depth_updated = gpuStats.numFlips; + gpuStats.perFrame.numDepthCopies++; + dest->last_frame_depth_updated = gpuStats.totals.numFlips; } else if (source.channel == RASTER_COLOR && draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) { VirtualFramebuffer *src = source.vfb; if (src->fb_format != GE_FORMAT_565) { @@ -730,7 +730,7 @@ void FramebufferManagerCommon::CopyToDepthFromOverlappingFramebuffers(VirtualFra shader = DRAW2D_565_TO_DEPTH_DESWIZZLE; } - gpuStats.numReinterpretCopies++; + gpuStats.perFrame.numReinterpretCopies++; src->usageFlags |= FB_USAGE_COLOR_MIXED_DEPTH; dest->usageFlags |= FB_USAGE_COLOR_MIXED_DEPTH; @@ -879,7 +879,7 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra const char *pass_name = "N/A"; float scaleFactorX = 1.0f; if (src->fb_format == dst->fb_format) { - gpuStats.numColorCopies++; + gpuStats.perFrame.numColorCopies++; pipeline = Get2DPipeline(DRAW2D_COPY_COLOR); pass_name = "copy_color"; } else { @@ -898,7 +898,7 @@ void FramebufferManagerCommon::CopyToColorFromOverlappingFramebuffers(VirtualFra pass_name = reinterpretStrings[(int)src->fb_format][(int)dst->fb_format]; - gpuStats.numReinterpretCopies++; + gpuStats.perFrame.numReinterpretCopies++; } if (pipeline) { @@ -1137,7 +1137,7 @@ void FramebufferManagerCommon::PerformWriteFormattedFromMemory(u32 addr, int siz VirtualFramebuffer *vfb = ResolveVFB(addr, stride, fmt); if (vfb) { // Let's count this as a "render". This will also force us to use the correct format. - vfb->last_frame_render = gpuStats.numFlips; + vfb->last_frame_render = gpuStats.totals.numFlips; vfb->colorBindSeq = GetBindSeqCount(); if (vfb->fb_stride < stride) { @@ -1173,7 +1173,7 @@ void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size) { if (useBufferedRendering_ && vfb->fbo) { GEBufferFormat fmt = vfb->fb_format; - if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) { + if (vfb->last_frame_render + 1 < gpuStats.totals.numFlips && isDisplayBuf) { // If we're not rendering to it, format may be wrong. Use displayFormat_ instead. // TODO: This doesn't seem quite right anymore. fmt = displayFormat_; @@ -1307,7 +1307,7 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF if (!partial && (flags & BINDFBCOLOR_UNCACHED) == 0) { currentFramebufferCopy_ = renderCopy; } - gpuStats.numCopiesForSelfTex++; + gpuStats.perFrame.numCopiesForSelfTex++; } else { // Failed to get temp FBO? Weird. draw_->BindFramebufferAsTexture(framebuffer->fbo, stage, Draw::Aspect::COLOR_BIT, layer); @@ -1487,7 +1487,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G for (auto &iter : drawPixelsCache_) { if (iter.contentsHash == imageHash && iter.tex->Width() == width && iter.tex->Height() == height && iter.tex->Format() == texFormat) { iter.frameNumber = frameNumber; - gpuStats.numCachedUploads++; + gpuStats.perFrame.numCachedUploads++; return iter.tex; } } @@ -1499,7 +1499,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G } // OK, current one seems good, let's use it (and mark it used). - gpuStats.numUploads++; + gpuStats.perFrame.numUploads++; draw_->UpdateTextureLevels(iter.tex, &srcPixels, generateTexture, 1); // NOTE: numFlips is no good - this is called every frame when paused sometimes! iter.frameNumber = frameNumber; @@ -1536,7 +1536,7 @@ Draw::Texture *FramebufferManagerCommon::MakePixelTexture(const u8 *srcPixels, G DrawPixelsEntry entry{ tex, imageHash, frameNumber }; drawPixelsCache_.push_back(entry); - gpuStats.numUploads++; + gpuStats.perFrame.numUploads++; return tex; } @@ -1677,7 +1677,7 @@ void FramebufferManagerCommon::PrepareCopyDisplayToOutput(const DisplayLayoutCon } vfb->usageFlags |= FB_USAGE_DISPLAYED_FRAMEBUFFER; - vfb->last_frame_displayed = gpuStats.numFlips; + vfb->last_frame_displayed = gpuStats.totals.numFlips; vfb->dirtyAfterDisplay = false; vfb->reallyDirtyAfterDisplay = false; @@ -1874,7 +1874,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, } return; } - if (!old.fbo && vfb->last_frame_failed != 0 && vfb->last_frame_failed - gpuStats.numFlips < 63) { + if (!old.fbo && vfb->last_frame_failed != 0 && vfb->last_frame_failed - gpuStats.totals.numFlips < 63) { // Don't constantly retry FBOs which failed to create. return; } @@ -1883,7 +1883,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, char tag[128]; size_t len = FormatFramebufferName(vfb, tag, sizeof(tag)); - gpuStats.numFBOsCreated++; + gpuStats.perFrame.numFBOsCreated++; vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), msaaLevel_, true, tag }); if (Memory::IsVRAMAddress(vfb->fb_address) && vfb->fb_stride != 0) { @@ -1915,7 +1915,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w, if (!vfb->fbo) { ERROR_LOG(Log::FrameBuf, "Error creating FBO during resize! %dx%d", vfb->renderWidth, vfb->renderHeight); - vfb->last_frame_failed = gpuStats.numFlips; + vfb->last_frame_failed = gpuStats.totals.numFlips; } } @@ -2024,7 +2024,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, continue; } - if ((u32)size > vfb_size + 0x1000 && vfb->fb_format != GE_FORMAT_8888 && vfb->last_frame_render < gpuStats.numFlips) { + if ((u32)size > vfb_size + 0x1000 && vfb->fb_format != GE_FORMAT_8888 && vfb->last_frame_render < gpuStats.totals.numFlips) { // Seems likely we are looking at a potential copy of 32-bit pixels (like video) to an old 16-bit buffer, // which is very likely simply the wrong target, so skip it. See issue #17740 where this happens in Naruto Ultimate Ninja Heroes 2. // Probably no point to give it a bad score and let it pass to sorting, as we're pretty sure here. @@ -2165,7 +2165,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } if (dstBuffer) { - dstBuffer->last_frame_used = gpuStats.numFlips; + dstBuffer->last_frame_used = gpuStats.totals.numFlips; if (channel == RASTER_DEPTH && !srcBuffer) dstBuffer->usageFlags |= FB_USAGE_COLOR_MIXED_DEPTH; } @@ -2185,7 +2185,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, return false; } else if (dstBuffer) { if (flags & GPUCopyFlag::MEMSET) { - gpuStats.numClears++; + gpuStats.perFrame.numClears++; WARN_LOG_N_TIMES(btucpy, 5, Log::FrameBuf, "Memcpy fbo memset-clear %08x (size: %x)", dst, size); } else { WARN_LOG_N_TIMES(btucpy, 5, Log::FrameBuf, "Memcpy fbo upload %08x -> %08x (size: %x)", src, dst, size); @@ -2411,7 +2411,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd vfb->height = height; vfb->newWidth = vfb->width; vfb->newHeight = vfb->height; - vfb->lastFrameNewSize = gpuStats.numFlips; + vfb->lastFrameNewSize = gpuStats.totals.numFlips; vfb->renderScaleFactor = renderScaleFactor_; vfb->renderWidth = (u16)(vfb->width * renderScaleFactor_); vfb->renderHeight = (u16)(vfb->height * renderScaleFactor_); @@ -2496,7 +2496,7 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram } nvfb->usageFlags |= FB_USAGE_RENDER_COLOR; - nvfb->last_frame_render = gpuStats.numFlips; + nvfb->last_frame_render = gpuStats.totals.numFlips; nvfb->dirtyAfterDisplay = true; return nvfb; @@ -2657,7 +2657,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst } if (dstBuffer) { - dstRect.vfb->last_frame_used = gpuStats.numFlips; + dstRect.vfb->last_frame_used = gpuStats.totals.numFlips; // Mark the destination as fresh. if (dstRect.channel == RASTER_COLOR) { dstRect.vfb->colorBindSeq = GetBindSeqCount(); @@ -2760,7 +2760,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst srcX1 /= scaleFactorX; srcX2 /= scaleFactorX; - gpuStats.numReinterpretCopies++; + gpuStats.perFrame.numReinterpretCopies++; FlushBeforeCopy(); BlitUsingRaster(src->fbo, srcX1, srcY1, srcX2, srcY2, dst->fbo, dstX1, dstY1, dstX2, dstY2, false, dst->renderScaleFactor, pipeline, pass_name); @@ -2875,7 +2875,7 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS // Make sure we don't flop back and forth. dstRect.vfb->newWidth = std::max(dstRect.w_bytes / bpp, (int)dstRect.vfb->width); dstRect.vfb->newHeight = std::max(dstRect.h, (int)dstRect.vfb->height); - dstRect.vfb->lastFrameNewSize = gpuStats.numFlips; + dstRect.vfb->lastFrameNewSize = gpuStats.totals.numFlips; // Resizing may change the viewport/etc. gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); } @@ -2979,7 +2979,7 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u u64 key = ((u64)reason << 48) | ((u32)w << 16) | h; auto it = tempFBOs_.find(key); if (it != tempFBOs_.end()) { - it->second.last_frame_used = gpuStats.numFlips; + it->second.last_frame_used = gpuStats.totals.numFlips; return it->second.fbo; } @@ -2992,7 +2992,7 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u return nullptr; } - const TempFBOInfo info = { fbo, gpuStats.numFlips }; + const TempFBOInfo info = { fbo, gpuStats.totals.numFlips }; tempFBOs_[key] = info; return fbo; } @@ -3255,9 +3255,9 @@ void FramebufferManagerCommon::ReadbackFramebuffer(VirtualFramebuffer *vfb, int NotifyMemInfo(MemBlockFlags::WRITE, fb_address + dstByteOffset, dstSize, tag, len); if (mode == Draw::ReadbackMode::BLOCK) { - gpuStats.numBlockingReadbacks++; + gpuStats.perFrame.numBlockingReadbacks++; } else { - gpuStats.numReadbacks++; + gpuStats.perFrame.numReadbacks++; } } @@ -3294,8 +3294,8 @@ void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, static int frameLastCopy = 0; static u32 bufferLastCopy = 0; static int copiesThisFrame = 0; - if (frameLastCopy != gpuStats.numFlips || bufferLastCopy != vfb->fb_address) { - frameLastCopy = gpuStats.numFlips; + if (frameLastCopy != gpuStats.totals.numFlips || bufferLastCopy != vfb->fb_address) { + frameLastCopy = gpuStats.totals.numFlips; bufferLastCopy = vfb->fb_address; copiesThisFrame = 0; } diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 883a51a77b..0072a48d18 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -313,7 +313,7 @@ public: VirtualFramebuffer *SetRenderFrameBuffer(bool framebufChanged, int skipDrawReason, bool *changed) { // Inlining this part since it's so frequent. if (!framebufChanged && currentRenderVfb_) { - currentRenderVfb_->last_frame_render = gpuStats.numFlips; + currentRenderVfb_->last_frame_render = gpuStats.totals.numFlips; currentRenderVfb_->dirtyAfterDisplay = true; if (!skipDrawReason) currentRenderVfb_->reallyDirtyAfterDisplay = true; diff --git a/GPU/Common/GPUDebugInterface.h b/GPU/Common/GPUDebugInterface.h index a76db41bf5..2e67408a39 100644 --- a/GPU/Common/GPUDebugInterface.h +++ b/GPU/Common/GPUDebugInterface.h @@ -214,6 +214,8 @@ struct GPUDebugVertex { float nz; }; +class StringWriter; + class GPUDebugInterface { public: virtual ~GPUDebugInterface() = default; @@ -236,7 +238,7 @@ public: virtual void SetCmdValue(u32 op) = 0; virtual void Flush() = 0; - virtual void GetStats(char *buffer, size_t bufsize) = 0; + virtual void GetStats(StringWriter &w) = 0; virtual uint32_t SetAddrTranslation(uint32_t value) = 0; virtual uint32_t GetAddrTranslation() = 0; diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index ab650da196..490177156f 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -233,7 +233,7 @@ void SoftwareTransform::Transform(int prim, u32 vertType, const DecVtxFormat &de result->color = transformed[1].color0_32; result->depth = depth; result->action = SW_CLEAR; - gpuStats.numClears++; + gpuStats.perFrame.numClears++; return; } } @@ -556,7 +556,7 @@ void SoftwareTransform::ProjectClipAndExpand(int prim, int vertexCount, u32 vert } if (gstate.isModeClear()) { - gpuStats.numClears++; + gpuStats.perFrame.numClears++; } result->action = SW_DRAW_INDEXED; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 67e9e99732..0dd630ea41 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -150,8 +150,8 @@ void TextureCacheCommon::StartFrame() { replacementFrameBudgetSeconds_ = baseValue / fps; if ((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS) { - gpuStats.numReplacerTrackedTex = replacer_.GetNumTrackedTextures(); - gpuStats.numCachedReplacedTextures = replacer_.GetNumCachedReplacedTextures(); + gpuStats.perFrame.numReplacerTrackedTex = replacer_.GetNumTrackedTextures(); + gpuStats.perFrame.numCachedReplacedTextures = replacer_.GetNumCachedReplacedTextures(); } if (texelsScaledThisFrame_) { @@ -511,8 +511,8 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { } if (match) { - if (entry->lastFrame != gpuStats.numFlips) { - u32 diff = gpuStats.numFlips - entry->lastFrame; + if (entry->lastFrame != gpuStats.totals.numFlips) { + u32 diff = gpuStats.totals.numFlips - entry->lastFrame; entry->numFrames++; if (entry->framesUntilNextFullHash < diff) { @@ -710,7 +710,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { } bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &entry, u32 texAddrOffset, AttachCandidate *bestCandidate, const char *context) const { - gpuStats.numFramebufferEvaluations++; + gpuStats.perFrame.numFramebufferEvaluations++; TinySet candidates; @@ -831,7 +831,7 @@ void TextureCacheCommon::Decimate(TexCacheEntry *exceptThisOne, bool forcePressu } bool hasClut = (iter->second->status & TexCacheEntry::STATUS_CLUT_VARIANTS) != 0; int killAge = hasClut ? TEXTURE_KILL_AGE_CLUT : killAgeBase; - if (iter->second->lastFrame + killAge < gpuStats.numFlips) { + if (iter->second->lastFrame + killAge < gpuStats.totals.numFlips) { DeleteTexture(iter++); } else { ++iter; @@ -851,7 +851,7 @@ void TextureCacheCommon::Decimate(TexCacheEntry *exceptThisOne, bool forcePressu continue; } // In low memory mode, we kill them all since secondary cache is disabled. - if (lowMemoryMode_ || iter->second->lastFrame + TEXTURE_SECOND_KILL_AGE < gpuStats.numFlips) { + if (lowMemoryMode_ || iter->second->lastFrame + TEXTURE_SECOND_KILL_AGE < gpuStats.totals.numFlips) { ReleaseTexture(iter->second.get(), true); secondCacheSizeEstimate_ -= EstimateTexMemoryUsage(iter->second.get()); iter = secondCache_.erase(iter); @@ -869,7 +869,7 @@ void TextureCacheCommon::Decimate(TexCacheEntry *exceptThisOne, bool forcePressu void TextureCacheCommon::DecimateVideos() { for (auto iter = videos_.begin(); iter != videos_.end(); ) { - if (iter->flips + VIDEO_DECIMATE_AGE < gpuStats.numFlips) { + if (iter->flips + VIDEO_DECIMATE_AGE < gpuStats.totals.numFlips) { iter = videos_.erase(iter); } else { ++iter; @@ -893,7 +893,7 @@ bool TextureCacheCommon::IsVideo(u32 texaddr) const { void TextureCacheCommon::HandleTextureChange(TexCacheEntry *const entry, const char *reason, bool initialMatch, bool doDelete) { cacheSizeEstimate_ -= EstimateTexMemoryUsage(entry); entry->numInvalidated++; - gpuStats.numTextureInvalidations++; + gpuStats.perFrame.numTextureInvalidations++; DEBUG_LOG(Log::G3D, "Texture different or overwritten, reloading at %08x: %s", entry->addr, reason); if (doDelete) { ForgetLastTexture(); @@ -956,7 +956,7 @@ void TextureCacheCommon::NotifyFramebuffer(VirtualFramebuffer *framebuffer, Fram // Color - no need to look in the mirrors. for (auto it = cache_.lower_bound(cacheKey), end = cache_.upper_bound(cacheKeyEnd); it != end; ++it) { it->second->status |= TexCacheEntry::STATUS_FRAMEBUFFER_OVERLAP; - gpuStats.numTextureInvalidationsByFramebuffer++; + gpuStats.perFrame.numTextureInvalidationsByFramebuffer++; } if (z_stride != 0) { @@ -966,11 +966,11 @@ void TextureCacheCommon::NotifyFramebuffer(VirtualFramebuffer *framebuffer, Fram cacheKeyEnd = (u64)z_endAddr << 32; for (auto it = cache_.lower_bound(cacheKey | 0x200000), end = cache_.upper_bound(cacheKeyEnd | 0x200000); it != end; ++it) { it->second->status |= TexCacheEntry::STATUS_FRAMEBUFFER_OVERLAP; - gpuStats.numTextureInvalidationsByFramebuffer++; + gpuStats.perFrame.numTextureInvalidationsByFramebuffer++; } for (auto it = cache_.lower_bound(cacheKey | 0x600000), end = cache_.upper_bound(cacheKeyEnd | 0x600000); it != end; ++it) { it->second->status |= TexCacheEntry::STATUS_FRAMEBUFFER_OVERLAP; - gpuStats.numTextureInvalidationsByFramebuffer++; + gpuStats.perFrame.numTextureInvalidationsByFramebuffer++; } } break; @@ -1179,7 +1179,7 @@ void TextureCacheCommon::SetTextureFramebuffer(const AttachCandidate &candidate) framebuffer->usageFlags |= FB_USAGE_TEXTURE; // Keep the framebuffer alive. - framebuffer->last_frame_used = gpuStats.numFlips; + framebuffer->last_frame_used = gpuStats.totals.numFlips; nextFramebufferTextureChannel_ = RASTER_COLOR; @@ -1335,7 +1335,7 @@ void TextureCacheCommon::NotifyConfigChanged() { void TextureCacheCommon::NotifyWriteFormattedFromMemory(u32 addr, int size, int width, GEBufferFormat fmt) { addr &= 0x3FFFFFFF; - videos_.push_back({ addr, (u32)size, gpuStats.numFlips }); + videos_.push_back({ addr, (u32)size, gpuStats.totals.numFlips }); } void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes, GPURecord::Recorder *recorder) { @@ -1394,14 +1394,14 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes, GPURecord::Record // the framebuffer with the smallest offset. This is yet another framebuffer matching // loop with its own rules, eventually we'll probably want to do something // more systematic. - bool okAge = !PSP_CoreParameter().compat.flags().LoadCLUTFromCurrentFrameOnly || framebuffer->last_frame_render == gpuStats.numFlips; // Here we can try heuristics. + bool okAge = !PSP_CoreParameter().compat.flags().LoadCLUTFromCurrentFrameOnly || framebuffer->last_frame_render == gpuStats.totals.numFlips; // Here we can try heuristics. if (matchRange && !inMargin && offset < (int)clutRenderOffset_) { if (okAge) { WARN_LOG_N_TIMES(clutfb, 5, Log::G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), last render %d frames ago, byte offset %d, pixel offset %d", - loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), gpuStats.numFlips - framebuffer->last_frame_render, offset, offset / fb_bpp); - framebuffer->last_frame_clut = gpuStats.numFlips; + loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), gpuStats.totals.numFlips - framebuffer->last_frame_render, offset, offset / fb_bpp); + framebuffer->last_frame_clut = gpuStats.totals.numFlips; // Also mark used so it's not decimated. - framebuffer->last_frame_used = gpuStats.numFlips; + framebuffer->last_frame_used = gpuStats.totals.numFlips; framebuffer->usageFlags |= FB_USAGE_CLUT; bestClutAddress = framebuffer->fb_address; clutRenderOffset_ = (u32)offset; @@ -1411,7 +1411,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes, GPURecord::Record break; } } else { - WARN_LOG(Log::G3D, "Ignoring CLUT load from %d frames old buffer at %08x", gpuStats.numFlips - framebuffer->last_frame_render, fb_address); + WARN_LOG(Log::G3D, "Ignoring CLUT load from %d frames old buffer at %08x", gpuStats.totals.numFlips - framebuffer->last_frame_render, fb_address); } } } @@ -2203,13 +2203,13 @@ void TextureCacheCommon::ApplyTexture(bool doBind) { if (entry->status & TexCacheEntry::STATUS_CLUT_GPU) { // Special process. ApplyTextureDepal(entry); - entry->lastFrame = gpuStats.numFlips; + entry->lastFrame = gpuStats.totals.numFlips; gstate_c.SetTextureFullAlpha(false); gstate_c.SetTextureIs3D(false); gstate_c.SetTextureIsArray(false); gstate_c.SetTextureIsBGRA(false); } else { - entry->lastFrame = gpuStats.numFlips; + entry->lastFrame = gpuStats.totals.numFlips; if (doBind) { BindTexture(entry); } @@ -2419,7 +2419,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer draw2D_->Blit(textureShader, u1, v1, u2, v2, u1, v1, u2, v2, framebuffer->renderWidth, framebuffer->renderHeight, depalWidth, framebuffer->renderHeight, false, framebuffer->renderScaleFactor); - gpuStats.numDepal++; + gpuStats.perFrame.numDepal++; gstate_c.curTextureWidth = texWidth; gstate_c.Dirty(DIRTY_UVSCALEOFFSET); @@ -2521,7 +2521,7 @@ void TextureCacheCommon::ApplyTextureDepal(TexCacheEntry *entry) { draw2D_->Blit(textureShader, u1, v1, u2, v2, u1, v1, u2, v2, texWidth, texHeight, texWidth, texHeight, false, 1); - gpuStats.numDepal++; + gpuStats.perFrame.numDepal++; gstate_c.curTextureWidth = texWidth; gstate_c.Dirty(DIRTY_UVSCALEOFFSET); @@ -2718,11 +2718,11 @@ void TextureCacheCommon::Invalidate(u32 addr, int size, GPUInvalidationType type entry->minihash = (entry->minihash ^ 0x89ABCDEF) + 89; } if (type != GPU_INVALIDATE_ALL) { - gpuStats.numTextureInvalidations++; + gpuStats.perFrame.numTextureInvalidations++; // Start it over from 0 (unless it's safe.) entry->numFrames = type == GPU_INVALIDATE_SAFE ? 256 : 0; if (type == GPU_INVALIDATE_SAFE) { - u32 diff = gpuStats.numFlips - entry->lastFrame; + u32 diff = gpuStats.totals.numFlips - entry->lastFrame; // We still need to mark if the texture is frequently changing, even if it's safely changing. if (diff < TEXCACHE_FRAME_CHANGE_FREQUENT) { entry->status |= TexCacheEntry::STATUS_CHANGE_FREQUENT; @@ -2770,7 +2770,7 @@ std::string AttachCandidate::ToString() const { } bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEntry *entry) { - gpuStats.numTexturesDecoded++; + gpuStats.perFrame.numTexturesDecoded++; // For the estimate, we assume cluts always point to 8888 for simplicity. cacheSizeEstimate_ += EstimateTexMemoryUsage(entry); diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 978dcbbc2b..42e92f91e2 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -485,7 +485,7 @@ protected: } const u32 *checkp = (const u32 *)Memory::GetPointer(addr); - gpuStats.numTextureDataBytesHashed += sizeInRAM; + gpuStats.perFrame.numTextureDataBytesHashed += sizeInRAM; if (Memory::IsValidAddress(addr + sizeInRAM)) { return StableQuickTexHash(checkp, sizeInRAM); diff --git a/GPU/Common/TextureShaderCommon.cpp b/GPU/Common/TextureShaderCommon.cpp index 08733466d1..757644db51 100644 --- a/GPU/Common/TextureShaderCommon.cpp +++ b/GPU/Common/TextureShaderCommon.cpp @@ -59,7 +59,7 @@ ClutTexture TextureShaderCache::GetClutTexture(GEPaletteFormat clutFormat, const auto oldtex = texCache_.find(clutId); if (oldtex != texCache_.end()) { - oldtex->second->lastFrame = gpuStats.numFlips; + oldtex->second->lastFrame = gpuStats.totals.numFlips; return *oldtex->second; } @@ -132,7 +132,7 @@ ClutTexture TextureShaderCache::GetClutTexture(GEPaletteFormat clutFormat, const } tex->texture = draw_->CreateTexture(desc); - tex->lastFrame = gpuStats.numFlips; + tex->lastFrame = gpuStats.totals.numFlips; texCache_[clutId] = tex; return *tex; @@ -187,7 +187,7 @@ Draw::SamplerState *TextureShaderCache::GetSampler(bool linearFilter) { void TextureShaderCache::Decimate() { for (auto tex = texCache_.begin(); tex != texCache_.end(); ) { - if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.numFlips) { + if (tex->second->lastFrame + DEPAL_TEXTURE_OLD_AGE < gpuStats.totals.numFlips) { tex->second->texture->Release(); delete tex->second; texCache_.erase(tex++); @@ -195,7 +195,7 @@ void TextureShaderCache::Decimate() { ++tex; } } - gpuStats.numClutTextures = (int)texCache_.size(); + gpuStats.perFrame.numClutTextures = (int)texCache_.size(); } Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETextureFormat textureFormat, GEBufferFormat bufferFormat, bool smoothedDepal, u32 depthUpperBits) { diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index de3c6bfb68..1873da61ca 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -304,7 +304,7 @@ void DrawEngineD3D11::Flush() { bool useElements; DecodeVerts(dec_, decoded_); DecodeIndsAndGetData(&prim, &vertexCount, &maxIndex, &useElements, false); - gpuStats.numUncachedVertsDrawn += vertexCount; + gpuStats.perFrame.numUncachedVertsDrawn += vertexCount; bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE; if (gstate.isModeThrough()) { @@ -392,7 +392,7 @@ void DrawEngineD3D11::Flush() { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } - gpuStats.numUncachedVertsDrawn += vertexCount; + gpuStats.perFrame.numUncachedVertsDrawn += vertexCount; prim = IndexGenerator::GeneralPrim((GEPrimitiveType)drawInds_[0].prim); VERBOSE_LOG(Log::G3D, "Flush prim %i SW! %i verts in one go", prim, vertexCount); diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 72f683e249..3bd73d7d72 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -20,6 +20,7 @@ #include "Common/Log.h" #include "Common/GraphicsContext.h" #include "Common/Profiler/Profiler.h" +#include "Common/Data/Text/StringWriter.h" #include "GPU/GPUState.h" @@ -140,14 +141,9 @@ void GPU_D3D11::FinishDeferred() { drawEngine_.FinishDeferred(); } -void GPU_D3D11::GetStats(char *buffer, size_t bufsize) { - size_t offset = FormatGPUStatsCommon(buffer, bufsize); - buffer += offset; - bufsize -= offset; - if ((int)bufsize < 0) - return; - snprintf(buffer, bufsize, - "Vertex, Fragment shaders loaded: %d, %d\n", +void GPU_D3D11::GetStats(StringWriter &w) { + FormatGPUStatsCommon(w); + w.F("Vertex, Fragment shaders loaded: %d, %d\n", shaderManagerD3D11_->GetNumVertexShaders(), shaderManagerD3D11_->GetNumFragmentShaders() ); diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index 511b63eb9d..c7a0cf3fe4 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -28,6 +28,7 @@ class FramebufferManagerD3D11; class ShaderManagerD3D11; class TextureCacheD3D11; +class StringWriter; class GPU_D3D11 : public GPUCommonHW { public: @@ -36,7 +37,7 @@ public: u32 CheckGPUFeatures() const override; - void GetStats(char *buffer, size_t bufsize) override; + void GetStats(StringWriter &w) override; void DeviceLost() override; // Destroy all device objects. void DeviceRestore(Draw::DrawContext *draw) override; diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index 38e3f86807..73b1d85171 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -135,7 +135,7 @@ bool Recorder::BeginRecording() { nextFrame = false; lastTextures.clear(); lastRenderTargets.clear(); - flipLastAction = gpuStats.numFlips; + flipLastAction = gpuStats.totals.numFlips; flipFinishAt = -1; u32 ptr = (u32)pushbuf.size(); @@ -575,7 +575,7 @@ void Recorder::EmitBezierSpline(u32 op) { bool Recorder::RecordNextFrame(const std::function callback) { if (!nextFrame) { - flipLastAction = gpuStats.numFlips; + flipLastAction = gpuStats.totals.numFlips; flipFinishAt = -1; writeCallback = callback; nextFrame = true; @@ -597,7 +597,7 @@ void Recorder::FinishRecording() { NOTICE_LOG(Log::System, "Recording finished"); active = false; - flipLastAction = gpuStats.numFlips; + flipLastAction = gpuStats.totals.numFlips; flipFinishAt = -1; lastEdramTrans = 0x400; @@ -785,9 +785,9 @@ void Recorder::NotifyDisplay(u32 framebuf, int stride, int fmt) { } void Recorder::NotifyBeginFrame() { - const bool noDisplayAction = flipLastAction + 4 < gpuStats.numFlips; + const bool noDisplayAction = flipLastAction + 4 < gpuStats.totals.numFlips; // We do this only to catch things that don't call NotifyDisplay. - if (active && HasDrawCommands() && (noDisplayAction || gpuStats.numFlips == flipFinishAt)) { + if (active && HasDrawCommands() && (noDisplayAction || gpuStats.totals.numFlips == flipFinishAt)) { NOTICE_LOG(Log::System, "Recording complete on frame"); CheckEdramTrans(); @@ -813,7 +813,7 @@ void Recorder::NotifyBeginFrame() { NOTICE_LOG(Log::System, "Recording starting on frame..."); BeginRecording(); // If we began on a BeginFrame, end on a BeginFrame. - flipFinishAt = gpuStats.numFlips + 1; + flipFinishAt = gpuStats.totals.numFlips + 1; } } diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 230787c710..05b355f4fa 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -337,7 +337,7 @@ void DrawEngineGLES::Flush() { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } - gpuStats.numUncachedVertsDrawn += vertexCount; + gpuStats.perFrame.numUncachedVertsDrawn += vertexCount; prim = IndexGenerator::GeneralPrim((GEPrimitiveType)drawInds_[0].prim); // We need correct viewport values in gstate_c already. diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index e5ef804410..973a3e58a2 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -48,7 +48,7 @@ void FragmentTestCacheGLES::BindTestTexture(int slot) { const FragmentTestID id = GenerateTestID(); const auto cached = cache_.find(id); if (cached != cache_.end()) { - cached->second.lastFrame = gpuStats.numFlips; + cached->second.lastFrame = gpuStats.totals.numFlips; GLRTexture *tex = cached->second.texture; if (tex == lastTexture_) { // Already bound, hurray. @@ -78,7 +78,7 @@ void FragmentTestCacheGLES::BindTestTexture(int slot) { // We only need to do this once for the texture. render_->SetTextureSampler(slot, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_NEAREST, GL_NEAREST, 0.0f); FragmentTestTexture item; - item.lastFrame = gpuStats.numFlips; + item.lastFrame = gpuStats.totals.numFlips; item.texture = tex; cache_[id] = item; } @@ -156,7 +156,7 @@ void FragmentTestCacheGLES::Clear(bool deleteThem) { void FragmentTestCacheGLES::Decimate() { if (--decimationCounter_ <= 0) { for (auto tex = cache_.begin(); tex != cache_.end(); ) { - if (tex->second.lastFrame + FRAGTEST_TEXTURE_OLD_AGE < gpuStats.numFlips) { + if (tex->second.lastFrame + FRAGTEST_TEXTURE_OLD_AGE < gpuStats.totals.numFlips) { render_->DeleteTexture(tex->second.texture); cache_.erase(tex++); } else { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index d1d49f2076..75c7462186 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -21,6 +21,7 @@ #include "Common/Log.h" #include "Common/Serialize/Serializer.h" #include "Common/File/FileUtil.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/GraphicsContext.h" #include "Common/System/OSD.h" #include "Common/VR/PPSSPPVR.h" @@ -248,7 +249,7 @@ void GPU_GLES::BeginHostFrame(const DisplayLayoutConfig &config) { // Save the cache from time to time. TODO: How often? We save on exit, so shouldn't need to do this all that often. constexpr int saveShaderCacheFrameInterval = 32767; // power of 2 - 1. About every 10 minutes at 60fps. - if (shaderCachePath_.Valid() && !(gpuStats.numFlips & saveShaderCacheFrameInterval) && coreState == CORE_RUNNING_CPU) { + if (shaderCachePath_.Valid() && !(gpuStats.totals.numFlips & saveShaderCacheFrameInterval) && coreState == CORE_RUNNING_CPU) { shaderManagerGL_->SaveCache(shaderCachePath_, &drawEngine_); } shaderManagerGL_->DirtyLastShader(); @@ -278,16 +279,11 @@ void GPU_GLES::FinishDeferred() { drawEngine_.FinishDeferred(); } -void GPU_GLES::GetStats(char *buffer, size_t bufsize) { - size_t offset = FormatGPUStatsCommon(buffer, bufsize); - buffer += offset; - bufsize -= offset; - if ((int)bufsize < 0) - return; - snprintf(buffer, bufsize, - "Vertex, Fragment, Programs loaded: %d, %d, %d\n", +void GPU_GLES::GetStats(StringWriter &w) { + w.F("Vertex, Fragment, Programs loaded: %d, %d, %d\n", shaderManagerGL_->GetNumVertexShaders(), shaderManagerGL_->GetNumFragmentShaders(), shaderManagerGL_->GetNumPrograms() ); + FormatGPUStatsCommon(w); } diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index a1c6056734..d013b01f16 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -39,7 +39,7 @@ public: // This gets called on startup and when we get back from settings. u32 CheckGPUFeatures() const override; - void GetStats(char *buffer, size_t bufsize) override; + void GetStats(StringWriter &w) override; void DeviceLost() override; // Only happens on Android. Drop all textures and shaders. void DeviceRestore(Draw::DrawContext *draw) override; diff --git a/GPU/GPU.h b/GPU/GPU.h index 107db847bd..09f6ca92d2 100644 --- a/GPU/GPU.h +++ b/GPU/GPU.h @@ -68,61 +68,8 @@ inline unsigned int toFloat24(float f) { return i >> 8; } -// The ToString function lives in GPUCommonHW.cpp. -struct GPUStatistics { - void Reset() { - ResetFrame(); - numFlips = 0; - } - - void ResetFrame() { - numDrawCalls = 0; - numVertexDecodes = 0; - numCulledDraws = 0; - numDrawSyncs = 0; - numListSyncs = 0; - numVertsSubmitted = 0; - numVertsDecoded = 0; - numUncachedVertsDrawn = 0; - numTextureInvalidations = 0; - numTextureInvalidationsByFramebuffer = 0; - numTexturesHashed = 0; - numTextureDataBytesHashed = 0; - numFlushes = 0; - numBBOXJumps = 0; - numTexturesDecoded = 0; - numFramebufferEvaluations = 0; - numFBOsCreated = 0; - numBlockingReadbacks = 0; - numReadbacks = 0; - numUploads = 0; - numCachedUploads = 0; - numDepal = 0; - numClears = 0; - numDepthCopies = 0; - numReinterpretCopies = 0; - numColorCopies = 0; - numCopiesForShaderBlend = 0; - numCopiesForSelfTex = 0; - numBlockTransfers = 0; - numReplacerTrackedTex = 0; - numCachedReplacedTextures = 0; - numClutTextures = 0; - msProcessingDisplayLists = 0; - msPrepareDepth = 0.0; - msCullDepth = 0.0; - msRasterizeDepth = 0.0; - msRasterTimeAvailable = 0.0; - numDepthRasterPrims = 0; - numDepthRasterEarlySize = 0; - numDepthRasterNoPixels = 0; - numDepthRasterTooSmall = 0; - numDepthRasterZCulled = 0; - numDepthEarlyBoxCulled = 0; - vertexGPUCycles = 0; - otherGPUCycles = 0; - } - +// TODO: Possibly use macros to disable expensive parts of stats tracking in release builds. +struct GPUStatsPerFrame { // Per frame statistics int numDrawCalls; int numVertexDecodes; @@ -169,10 +116,28 @@ struct GPUStatistics { int numDepthRasterTooSmall; int numDepthRasterZCulled; int numDepthEarlyBoxCulled; - // Flip count. Doesn't really belong here. +}; + +struct GPUStatsTotals { int numFlips; }; +// The ToString function lives in GPUCommonHW.cpp. +struct GPUStatistics { + void Reset() { + ResetFrame(); + totals = {}; + } + + void ResetFrame() { + perFrame = {}; + } + + // Flip count. Doesn't really belong here. + GPUStatsPerFrame perFrame; + GPUStatsTotals totals; +}; + extern GPUStatistics gpuStats; extern GPUCommon *gpu; extern GPUDebugInterface *gpuDebug; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 7b0b363efe..2716e453a7 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -55,7 +55,7 @@ GPUCommon::GPUCommon(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : Reinitialize(); gstate.Reset(); gstate_c.Reset(); - gpuStats.Reset(); + gpuStats.ResetFrame(); PPGeSetDrawContext(draw); ResetMatrices(); @@ -172,7 +172,7 @@ void GPUCommon::DumpNextFrame() { } u32 GPUCommon::DrawSync(int mode) { - gpuStats.numDrawSyncs++; + gpuStats.perFrame.numDrawSyncs++; if (mode < 0 || mode > 1) return SCE_KERNEL_ERROR_INVALID_MODE; @@ -222,7 +222,7 @@ void GPUCommon::CheckDrawSync() { } int GPUCommon::ListSync(int listid, int mode) { - gpuStats.numListSyncs++; + gpuStats.perFrame.numListSyncs++; if (listid < 0 || listid >= DisplayListMaxCount) return SCE_KERNEL_ERROR_INVALID_ID; @@ -737,7 +737,7 @@ DLResult GPUCommon::ProcessDLQueue() { } } - TimeCollector collectStat(&gpuStats.msProcessingDisplayLists, coreCollectDebugStats); + TimeCollector collectStat(&gpuStats.perFrame.msProcessingDisplayLists, coreCollectDebugStats); auto GetNextListIndex = [&]() -> int { if (dlQueue.empty()) @@ -866,7 +866,7 @@ DLResult GPUCommon::ProcessDLQueue() { currentList = nullptr; if (coreCollectDebugStats) { - gpuStats.otherGPUCycles += cyclesExecuted; + gpuStats.perFrame.otherGPUCycles += cyclesExecuted; } drawCompleteTicks = startingTicks + cyclesExecuted; @@ -915,7 +915,7 @@ void GPUCommon::Execute_BJump(u32 op, u32 diff) { if (!currentList->bboxResult) { // bounding box jump. const u32 target = gstate_c.getRelativeAddress(op & 0x00FFFFFC); - gpuStats.numBBOXJumps++; + gpuStats.perFrame.numBBOXJumps++; if (Memory::IsValidAddress(target)) { UpdatePC(currentList->pc, target - 4); currentList->pc = target - 4; // pc will be increased after we return, counteract that @@ -1400,7 +1400,7 @@ void GPUCommon::FastLoadBoneMatrix(u32 target) { cyclesExecuted += 2 * 14; // one to reset the counter, 12 to load the matrix, and a return. if (coreCollectDebugStats) { - gpuStats.otherGPUCycles += 2 * 14; + gpuStats.perFrame.otherGPUCycles += 2 * 14; } } @@ -1720,7 +1720,7 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { int bpp = gstate.getTransferBpp(); DEBUG_LOG(Log::G3D, "Block transfer: %08x/%x -> %08x/%x, %ix%ix%i (%i,%i)->(%i,%i)", srcBasePtr, srcStride, dstBasePtr, dstStride, width, height, bpp, srcX, srcY, dstX, dstY); - gpuStats.numBlockTransfers++; + gpuStats.perFrame.numBlockTransfers++; // For VRAM, we wrap around when outside valid memory (mirrors still work.) if ((srcBasePtr & 0x04800000) == 0x04800000) @@ -2090,10 +2090,10 @@ GPUDebug::NotifyResult GPUCommon::NotifyCommand(u32 pc, GPUBreakpoints *breakpoi u32 op = Memory::ReadUnchecked_U32(pc); u32 cmd = op >> 24; - if (thisFlipNum_ != gpuStats.numFlips) { + if (thisFlipNum_ != gpuStats.totals.numFlips) { primsLastFrame_ = primsThisFrame_; primsThisFrame_ = 0; - thisFlipNum_ = gpuStats.numFlips; + thisFlipNum_ = gpuStats.totals.numFlips; } bool isPrim = false; diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 06dea0b896..53ce543832 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -3,6 +3,7 @@ #include "Common/GPU/thin3d.h" #include "Common/Serialize/Serializer.h" #include "Common/System/System.h" +#include "Common/Data/Text/StringWriter.h" #include "Core/System.h" #include "Core/Config.h" @@ -770,9 +771,9 @@ void GPUCommonHW::CheckDepthUsage(VirtualFramebuffer *vfb) { if (isWritingDepth || isReadingDepth) { gstate_c.usingDepth = true; gstate_c.clearingDepth = isClearingDepth; - vfb->last_frame_depth_render = gpuStats.numFlips; + vfb->last_frame_depth_render = gpuStats.totals.numFlips; if (isWritingDepth) { - vfb->last_frame_depth_updated = gpuStats.numFlips; + vfb->last_frame_depth_updated = gpuStats.totals.numFlips; } framebufferManager_->SetDepthFrameBuffer(isClearingDepth); } @@ -980,7 +981,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { // Rough estimate, not sure what's correct. cyclesExecuted += vertexCost_ * count; if (gstate.isModeClear()) { - gpuStats.numClears++; + gpuStats.perFrame.numClears++; } return; } @@ -1027,9 +1028,9 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { if ((vertexType & (GE_VTYPE_THROUGH_MASK | GE_VTYPE_POS_MASK | GE_VTYPE_IDX_MASK)) == (GE_VTYPE_THROUGH_MASK | GE_VTYPE_POS_FLOAT | GE_VTYPE_IDX_NONE)) { int bytesRead = 0; if (!drawEngineCommon_->TestBoundingBoxThrough(verts, count, decoder, vertexType, &bytesRead)) { - gpuStats.numCulledDraws++; + gpuStats.perFrame.numCulledDraws++; int cycles = vertexCost_ * count; - gpuStats.vertexGPUCycles += cycles; + gpuStats.perFrame.vertexGPUCycles += cycles; cyclesExecuted += cycles; // NOTE! We still have to advance vertex pointers! gstate_c.vertexAddr += bytesRead; // We know from the above check that it's not an indexed draw. @@ -1057,7 +1058,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { if (drawEngineCommon_->TestBoundingBoxFast(gstate_c.worldviewproj, verts, count, decoder, vertexType)) { passCulling = true; } else { - gpuStats.numCulledDraws++; + gpuStats.perFrame.numCulledDraws++; } } @@ -1147,7 +1148,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { if (drawEngineCommon_->TestBoundingBoxFast(gstate_c.worldviewproj, verts, count, decoder, vertexType)) { passCulling = true; } else { - gpuStats.numCulledDraws++; + gpuStats.perFrame.numCulledDraws++; } } if (passCulling) { @@ -1301,7 +1302,7 @@ bail: } int cycles = vertexCost_ * totalVertCount; - gpuStats.vertexGPUCycles += cycles; + gpuStats.perFrame.vertexGPUCycles += cycles; if (!PSP_CoreParameter().compat.flags().FastEmulatedGPU) { cyclesExecuted += cycles; @@ -1825,9 +1826,9 @@ int GPUCommonHW::ListSync(int listid, int mode) { return GPUCommon::ListSync(listid, mode); } -size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) { - float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f; - return snprintf(buffer, size, +void GPUCommonHW::FormatGPUStatsCommon(StringWriter &w) { + float vertexAverageCycles = gpuStats.perFrame.numVertsSubmitted > 0 ? (float)gpuStats.perFrame.vertexGPUCycles / (float)gpuStats.perFrame.numVertsSubmitted : 0.0f; + w.F( "DL processing time: %0.2f ms, %d drawsync, %d listsync\n" "Draw: %d (%d dec, %d culled), flushes %d, clears %d, bbox jumps %d\n" "Vertices: %d dec: %d drawn: %d\n" @@ -1840,52 +1841,52 @@ size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) { "GPU cycles: %d (%0.1f per vertex)\n" "Z-rast: %0.2f+%0.2f+%0.2f (total %0.2f/%0.2f) ms\n" "Z-rast: %d prim, %d nopix, %d small, %d earlysize, %d zcull, %d box\n%s", - gpuStats.msProcessingDisplayLists * 1000.0f, - gpuStats.numDrawSyncs, - gpuStats.numListSyncs, - gpuStats.numDrawCalls, - gpuStats.numVertexDecodes, - gpuStats.numCulledDraws, - gpuStats.numFlushes, - gpuStats.numClears, - gpuStats.numBBOXJumps, - gpuStats.numVertsSubmitted, - gpuStats.numVertsDecoded, - gpuStats.numUncachedVertsDrawn, + gpuStats.perFrame.msProcessingDisplayLists * 1000.0f, + gpuStats.perFrame.numDrawSyncs, + gpuStats.perFrame.numListSyncs, + gpuStats.perFrame.numDrawCalls, + gpuStats.perFrame.numVertexDecodes, + gpuStats.perFrame.numCulledDraws, + gpuStats.perFrame.numFlushes, + gpuStats.perFrame.numClears, + gpuStats.perFrame.numBBOXJumps, + gpuStats.perFrame.numVertsSubmitted, + gpuStats.perFrame.numVertsDecoded, + gpuStats.perFrame.numUncachedVertsDrawn, (int)framebufferManager_->NumVFBs(), - gpuStats.numFramebufferEvaluations, - gpuStats.numFBOsCreated, + gpuStats.perFrame.numFramebufferEvaluations, + gpuStats.perFrame.numFBOsCreated, (int)textureCache_->NumLoadedTextures(), - gpuStats.numTexturesDecoded, - gpuStats.numTextureInvalidations, - gpuStats.numTextureDataBytesHashed / 1024, - gpuStats.numClutTextures, - gpuStats.numBlockingReadbacks, - gpuStats.numReadbacks, - gpuStats.numUploads, - gpuStats.numCachedUploads, - gpuStats.numDepal, - gpuStats.numBlockTransfers, - gpuStats.numReplacerTrackedTex, - gpuStats.numCachedReplacedTextures, - gpuStats.numDepthCopies, - gpuStats.numColorCopies, - gpuStats.numReinterpretCopies, - gpuStats.numCopiesForShaderBlend, - gpuStats.numCopiesForSelfTex, - gpuStats.vertexGPUCycles + gpuStats.otherGPUCycles, + gpuStats.perFrame.numTexturesDecoded, + gpuStats.perFrame.numTextureInvalidations, + gpuStats.perFrame.numTextureDataBytesHashed / 1024, + gpuStats.perFrame.numClutTextures, + gpuStats.perFrame.numBlockingReadbacks, + gpuStats.perFrame.numReadbacks, + gpuStats.perFrame.numUploads, + gpuStats.perFrame.numCachedUploads, + gpuStats.perFrame.numDepal, + gpuStats.perFrame.numBlockTransfers, + gpuStats.perFrame.numReplacerTrackedTex, + gpuStats.perFrame.numCachedReplacedTextures, + gpuStats.perFrame.numDepthCopies, + gpuStats.perFrame.numColorCopies, + gpuStats.perFrame.numReinterpretCopies, + gpuStats.perFrame.numCopiesForShaderBlend, + gpuStats.perFrame.numCopiesForSelfTex, + gpuStats.perFrame.vertexGPUCycles + gpuStats.perFrame.otherGPUCycles, vertexAverageCycles, - gpuStats.msPrepareDepth * 1000.0, - gpuStats.msCullDepth * 1000.0, - gpuStats.msRasterizeDepth * 1000.0, - (gpuStats.msPrepareDepth + gpuStats.msCullDepth + gpuStats.msRasterizeDepth) * 1000.0, - gpuStats.msRasterTimeAvailable * 1000.0, - gpuStats.numDepthRasterPrims, - gpuStats.numDepthRasterNoPixels, - gpuStats.numDepthRasterTooSmall, - gpuStats.numDepthRasterEarlySize, - gpuStats.numDepthRasterZCulled, - gpuStats.numDepthEarlyBoxCulled, + gpuStats.perFrame.msPrepareDepth * 1000.0, + gpuStats.perFrame.msCullDepth * 1000.0, + gpuStats.perFrame.msRasterizeDepth * 1000.0, + (gpuStats.perFrame.msPrepareDepth + gpuStats.perFrame.msCullDepth + gpuStats.perFrame.msRasterizeDepth) * 1000.0, + gpuStats.perFrame.msRasterTimeAvailable * 1000.0, + gpuStats.perFrame.numDepthRasterPrims, + gpuStats.perFrame.numDepthRasterNoPixels, + gpuStats.perFrame.numDepthRasterTooSmall, + gpuStats.perFrame.numDepthRasterEarlySize, + gpuStats.perFrame.numDepthRasterZCulled, + gpuStats.perFrame.numDepthEarlyBoxCulled, debugRecording_ ? "(debug-recording)" : "" ); } diff --git a/GPU/GPUCommonHW.h b/GPU/GPUCommonHW.h index 6b1032c91e..b795c1d37e 100644 --- a/GPU/GPUCommonHW.h +++ b/GPU/GPUCommonHW.h @@ -2,6 +2,8 @@ #include "GPUCommon.h" +class StringWriter; + // Shared GPUCommon implementation for the HW backends. // Things that are irrelevant for SoftGPU should live here. class GPUCommonHW : public GPUCommon { @@ -88,7 +90,7 @@ private: void CheckFlushOp(int cmd, u32 diff); protected: - size_t FormatGPUStatsCommon(char *buf, size_t size); + void FormatGPUStatsCommon(StringWriter &w); void UpdateCmdInfo() override; void PreExecuteOp(u32 op, u32 diff) override; diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 25eb866939..cdb5e9920f 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -17,6 +17,8 @@ #pragma once +// Note: GPU statistics are in GPU.h. + #include "ppsspp_config.h" #include "Common/CommonTypes.h" diff --git a/GPU/Software/BinManager.cpp b/GPU/Software/BinManager.cpp index 8b4f79c64b..fddafdd28e 100644 --- a/GPU/Software/BinManager.cpp +++ b/GPU/Software/BinManager.cpp @@ -21,6 +21,7 @@ #include "Common/Profiler/Profiler.h" #include "Common/Thread/ThreadManager.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/TimeUtil.h" #include "Core/System.h" #include "GPU/Common/TextureDecoder.h" @@ -185,8 +186,8 @@ void BinManager::UpdateState() { ClearDirty(SoftDirty::PIXEL_ALL | SoftDirty::SAMPLER_ALL | SoftDirty::RAST_ALL); } - if (lastFlipstats_ != gpuStats.numFlips) { - lastFlipstats_ = gpuStats.numFlips; + if (lastFlipstats_ != gpuStats.totals.numFlips) { + lastFlipstats_ = gpuStats.totals.numFlips; ResetStats(); } @@ -672,7 +673,7 @@ bool BinManager::HasPendingRead(uint32_t start, uint32_t stride, uint32_t w, uin return false; } -void BinManager::GetStats(char *buffer, size_t bufsize) { +void BinManager::GetStats(StringWriter &w) { double allTotal = 0.0; double slowestTotalTime = 0.0; const char *slowestTotalReason = nullptr; @@ -695,9 +696,7 @@ void BinManager::GetStats(char *buffer, size_t bufsize) { } recentTotal += it.second; } - - snprintf(buffer, bufsize, - "Slowest individual flush: %s (%0.4f)\n" + w.F("Slowest individual flush: %s (%0.4f)\n" "Slowest frame flush: %s (%0.4f)\n" "Slowest recent flush: %s (%0.4f)\n" "Total flush time: %0.4f (%05.2f%%, last 2: %05.2f%%)\n" diff --git a/GPU/Software/BinManager.h b/GPU/Software/BinManager.h index 45f5f964e2..73f9d5c8a5 100644 --- a/GPU/Software/BinManager.h +++ b/GPU/Software/BinManager.h @@ -182,6 +182,7 @@ struct BinDirtyRange { void Expand(uint32_t newBase, uint32_t bpp, uint32_t stride, const DrawingCoords &tl, const DrawingCoords &br); }; +class StringWriter; class BinManager { public: BinManager(); @@ -207,7 +208,7 @@ public: // Assumes you've also checked for a write (writes are partial so are automatically reads.) bool HasPendingRead(uint32_t start, uint32_t stride, uint32_t w, uint32_t h); - void GetStats(char *buffer, size_t bufsize); + void GetStats(StringWriter &w); void ResetStats(); void SetDirty(SoftDirty flags) { diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 02848f4bc9..e79b8fc39e 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -1273,8 +1273,8 @@ u32 SoftGPU::DrawSync(int mode) { return GPUCommon::DrawSync(mode); } -void SoftGPU::GetStats(char *buffer, size_t bufsize) { - drawEngine_->transformUnit.GetStats(buffer, bufsize); +void SoftGPU::GetStats(StringWriter &w) { + drawEngine_->transformUnit.GetStats(w); } void SoftGPU::InvalidateCache(u32 addr, int size, GPUInvalidationType type) diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 639e80ce6f..e75e9deac7 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -139,7 +139,7 @@ public: void SetCurFramebufferDirty(bool dirty) override {} void PrepareCopyDisplayToOutput(const DisplayLayoutConfig &config) override; void CopyDisplayToOutput(const DisplayLayoutConfig &config) override; - void GetStats(char *buffer, size_t bufsize) override; + void GetStats(StringWriter &w) override; std::vector GetFramebufferList() const override { return std::vector(); } void InvalidateCache(u32 addr, int size, GPUInvalidationType type) override; void PerformWriteFormattedFromMemory(u32 addr, int size, int width, GEBufferFormat format) override; diff --git a/GPU/Software/TransformUnit.cpp b/GPU/Software/TransformUnit.cpp index cdabe8a1f9..3f6f31f257 100644 --- a/GPU/Software/TransformUnit.cpp +++ b/GPU/Software/TransformUnit.cpp @@ -892,9 +892,9 @@ void TransformUnit::Flush(GPUCommon *common, const char *reason) { hasDraws_ = false; } -void TransformUnit::GetStats(char *buffer, size_t bufsize) { +void TransformUnit::GetStats(StringWriter &w) { // TODO: More stats? - binner_->GetStats(buffer, bufsize); + binner_->GetStats(w); } void TransformUnit::FlushIfOverlap(GPUCommon *common, const char *reason, bool modifying, uint32_t addr, uint32_t stride, uint32_t w, uint32_t h) { diff --git a/GPU/Software/TransformUnit.h b/GPU/Software/TransformUnit.h index 29b1d07f54..eeb7b02e72 100644 --- a/GPU/Software/TransformUnit.h +++ b/GPU/Software/TransformUnit.h @@ -111,6 +111,7 @@ class VertexReader; class SoftwareDrawEngine; class SoftwareVertexReader; +class StringWriter; class TransformUnit { public: @@ -141,7 +142,7 @@ public: void FlushIfOverlap(GPUCommon *common, const char *reason, bool modifying, uint32_t addr, uint32_t stride, uint32_t w, uint32_t h); void NotifyClutUpdate(const void *src); - void GetStats(char *buffer, size_t bufsize); + void GetStats(StringWriter &w); void SetDirty(SoftDirty flags); SoftDirty GetDirty(); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 416184806e..79e911d500 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -395,7 +395,7 @@ void DrawEngineVulkan::Flush() { gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && ((hasColor && (gstate.materialupdate & 1)) || gstate.getMaterialAmbientA() == 255) && (!gstate.isLightingEnabled() || gstate.getAmbientA() == 255); } - gpuStats.numUncachedVertsDrawn += vertexCount; + gpuStats.perFrame.numUncachedVertsDrawn += vertexCount; prim = IndexGenerator::GeneralPrim((GEPrimitiveType)drawInds_[0].prim); // At this point, the output is always an indexed triangle/line/point list, no strips/fans. diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 0f8246b506..799103fe53 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -25,6 +25,7 @@ #include "Common/File/FileUtil.h" #include "Common/GraphicsContext.h" #include "Common/StringUtils.h" +#include "Common/Data/Text/StringWriter.h" #include "Core/Config.h" #include "Core/Reporting.h" @@ -468,26 +469,17 @@ void GPU_Vulkan::DeviceRestore(Draw::DrawContext *draw) { InitDeviceObjects(); } -void GPU_Vulkan::GetStats(char *buffer, size_t bufsize) { - size_t offset = FormatGPUStatsCommon(buffer, bufsize); - buffer += offset; - bufsize -= offset; - if ((int)bufsize < 0) - return; +void GPU_Vulkan::GetStats(StringWriter &w) { const DrawEngineVulkanStats &drawStats = drawEngine_.GetStats(); - char texStats[256]; - textureCacheVulkan_->GetStats(texStats, sizeof(texStats)); - snprintf(buffer, bufsize, - "Vertex, Fragment, Pipelines loaded: %i, %i, %i\n" - "Pushbuffer space used: Vtx %d, Idx %d\n" - "%s\n", + w.F("Vertex, Fragment, Pipelines loaded: %i, %i, %i\n" + "Pushbuffer space used: Vtx %d, Idx %d\n", shaderManagerVulkan_->GetNumVertexShaders(), shaderManagerVulkan_->GetNumFragmentShaders(), pipelineManager_->GetNumPipelines(), drawStats.pushVertexSpaceUsed, - drawStats.pushIndexSpaceUsed, - texStats - ); + drawStats.pushIndexSpaceUsed); + textureCacheVulkan_->GetStats(w); + FormatGPUStatsCommon(w); } std::vector GPU_Vulkan::DebugGetShaderIDs(DebugShaderType type) { diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 25475bc477..9ff6816ab0 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -32,6 +32,7 @@ class FramebufferManagerVulkan; class ShaderManagerVulkan; class LinkedShader; class TextureCacheVulkan; +class StringWriter; class GPU_Vulkan : public GPUCommonHW { public: @@ -47,7 +48,7 @@ public: void BeginHostFrame(const DisplayLayoutConfig &config) override; void EndHostFrame() override; - void GetStats(char *buffer, size_t bufsize) override; + void GetStats(StringWriter &w) override; void DeviceLost() override; // Only happens on Android. Drop all textures and shaders. void DeviceRestore(Draw::DrawContext *draw) override; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 242421e7f2..859963af67 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -1111,9 +1111,7 @@ bool TextureCacheVulkan::GetCurrentTextureDebug(GPUDebugBuffer &buffer, int leve return true; } -void TextureCacheVulkan::GetStats(char *ptr, size_t size) { - snprintf(ptr, size, "N/A"); -} +void TextureCacheVulkan::GetStats(StringWriter &w) {} std::vector TextureCacheVulkan::DebugGetSamplerIDs() const { return samplerCache_.DebugGetSamplerIDs(); diff --git a/GPU/Vulkan/TextureCacheVulkan.h b/GPU/Vulkan/TextureCacheVulkan.h index 20c5bbe299..58f458cff7 100644 --- a/GPU/Vulkan/TextureCacheVulkan.h +++ b/GPU/Vulkan/TextureCacheVulkan.h @@ -33,6 +33,7 @@ class DrawEngineVulkan; class VulkanContext; class VulkanTexture; +class StringWriter; class SamplerCache { public: @@ -76,7 +77,7 @@ public: bool GetCurrentTextureDebug(GPUDebugBuffer &buffer, int level, bool *isFramebuffer) override; - void GetStats(char *ptr, size_t size); + void GetStats(StringWriter &w); std::vector DebugGetSamplerIDs() const; std::string DebugGetSamplerString(const std::string &id, DebugShaderStringType stringType); diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index 5e517c8910..85d5ebe1c3 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -4,7 +4,7 @@ #include "Common/Data/Text/I18n.h" #include "Common/CPUDetect.h" #include "Common/StringUtils.h" -#include "Common/Data/Text/Parsers.h" +#include "Common/Data/Text/StringWriter.h" #include "Core/MIPS/MIPS.h" #include "Core/HW/Display.h" @@ -46,9 +46,20 @@ static void DrawDebugStats(UIContext *ctx, const Bounds &bounds) { ctx->BindFontTexture(); ctx->Draw()->SetFontScale(.7f, .7f); - __DisplayGetDebugStats(statbuf, sizeof(statbuf)); - ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 11, bounds.y + 31, left, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII); - ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 10, bounds.y + 30, left, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + 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(); @@ -58,7 +69,7 @@ static void DrawDebugStats(UIContext *ctx, const Bounds &bounds) { static void DrawAudioDebugStats(UIContext *ctx, const Bounds &bounds) { FontID ubuntu24("UBUNTU24"); - char statbuf[4096] = { 0 }; + char statbuf[4096]; System_AudioGetDebugStats(statbuf, sizeof(statbuf)); ctx->Flush(); @@ -82,14 +93,15 @@ static void DrawAudioDebugStats(UIContext *ctx, const Bounds &bounds) { static void DrawControlDebug(UIContext *ctx, const ControlMapper &mapper, const Bounds &bounds) { FontID ubuntu24("UBUNTU24"); - char statbuf[4096] = { 0 }; - mapper.GetDebugString(statbuf, sizeof(statbuf)); + 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, 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); + 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(); diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 8e8ffc073b..f27f8ab22c 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -7,7 +7,7 @@ #include "Common/StringUtils.h" #include "Common/File/FileUtil.h" #include "Common/Data/Format/IniFile.h" -#include "Common/Data/Text/Parsers.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/Log/LogManager.h" #include "Common/TimeUtil.h" #include "Core/Config.h" @@ -2273,7 +2273,7 @@ void DrawHLEModules(ImConfig &config) { break; } } - w.F("%s 0x%08x %d %s", func.name, func.ID, strlen(func.argmask), amask.c_str()).endl(); + w.F("%s 0x%08x %d %s", func.name, func.ID, (int)strlen(func.argmask), amask.c_str()).endl(); } System_CopyStringToClipboard(w.as_view()); delete[] buffer; diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index ab38ea7eac..794f5f81ac 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -3,6 +3,7 @@ #include "ext/imgui/imgui_extras.h" #include "ext/imgui/imgui_impl_thin3d.h" #include "Common/Data/Convert/ColorConv.h" +#include "Common/Data/Text/StringWriter.h" #include "UI/ImDebugger/ImGe.h" #include "UI/ImDebugger/ImDebugger.h" #include "GPU/Common/GPUDebugInterface.h" @@ -33,7 +34,7 @@ void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebuffer return; } - ImGui::Text("Cur frame: %d seq: %d", gpuStats.numFlips, framebufferManager->PeekBindSeqCount()); + ImGui::Text("Cur frame: %d seq: %d", gpuStats.totals.numFlips, framebufferManager->PeekBindSeqCount()); ImGui::BeginTable("framebuffers", 7); ImGui::TableSetupColumn("Tag", ImGuiTableColumnFlags_WidthFixed); @@ -282,7 +283,8 @@ void DrawDebugStatsWindow(ImConfig &cfg) { return; } char statbuf[4096]; - __DisplayGetDebugStats(statbuf, sizeof(statbuf)); + StringWriter w(statbuf); + __DisplayGetDebugStats(w); ImGui::TextUnformatted(statbuf); ImGui::End(); } diff --git a/UI/SystemInfoScreen.cpp b/UI/SystemInfoScreen.cpp index 9c56946120..b31cabf19b 100644 --- a/UI/SystemInfoScreen.cpp +++ b/UI/SystemInfoScreen.cpp @@ -13,7 +13,7 @@ #include "Common/File/AndroidStorage.h" #include "Common/Audio/AudioBackend.h" #include "Common/Data/Text/I18n.h" -#include "Common/Data/Text/Parsers.h" +#include "Common/Data/Text/StringWriter.h" #include "Common/Data/Encoding/Utf8.h" #include "Common/Render/Text/draw_text.h" #include "Common/System/Request.h"