mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
180 lines
4.9 KiB
C++
180 lines
4.9 KiB
C++
#include <cstdarg>
|
|
|
|
#include "Common/GPU/OpenGL/GLProfiler.h"
|
|
#include "Common/GPU/OpenGL/GLCommon.h"
|
|
#include "Common/GPU/OpenGL/GLFeatures.h"
|
|
#include "Common/GPU/OpenGL/GLDebugLog.h"
|
|
#include "Common/Log.h"
|
|
|
|
// For iOS, define function pointer types and variables locally since gl3stub.h
|
|
// content is excluded on iOS. These will remain NULL since iOS doesn't support
|
|
// GL_EXT_disjoint_timer_query.
|
|
#if PPSSPP_PLATFORM(IOS)
|
|
typedef void (*PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target);
|
|
typedef void (*PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 *params);
|
|
static PFNGLQUERYCOUNTERPROC glQueryCounter = nullptr;
|
|
static PFNGLGETQUERYOBJECTUI64VPROC glGetQueryObjectui64v = nullptr;
|
|
#endif
|
|
|
|
// Constants - same values for both EXT and ARB versions
|
|
#ifndef GL_TIMESTAMP
|
|
#define GL_TIMESTAMP 0x8E28
|
|
#endif
|
|
#ifndef GL_QUERY_RESULT
|
|
#define GL_QUERY_RESULT 0x8866
|
|
#endif
|
|
#ifndef GL_GPU_DISJOINT_EXT
|
|
#define GL_GPU_DISJOINT_EXT 0x8FBB
|
|
#endif
|
|
|
|
// GLCommon.h provides access to GL functions:
|
|
// - On GLES: function pointers from gl3stub.h, loaded via eglGetProcAddress
|
|
// - On desktop GL: GLEW provides the functions
|
|
|
|
void GLProfiler::Init() {
|
|
supported_ = false;
|
|
firstFrame_ = true;
|
|
numQueries_ = 0;
|
|
scopes_.clear();
|
|
scopeStack_.clear();
|
|
|
|
// NOTE: Getting a lot of hangs on Android from this, I'm disabling it entirely for now.
|
|
// Will later re-enable on desktop.
|
|
#if 0
|
|
|
|
// Check for extension support
|
|
// Function pointers are declared in gl3stub.h and loaded appropriately per platform
|
|
if (gl_extensions.EXT_disjoint_timer_query) {
|
|
// GLES path - use function pointers loaded with EXT suffix
|
|
if (glQueryCounter && glGetQueryObjectui64v) {
|
|
supported_ = true;
|
|
INFO_LOG(Log::G3D, "GLProfiler: Using GL_EXT_disjoint_timer_query");
|
|
}
|
|
} else if (gl_extensions.ARB_timer_query) {
|
|
// Desktop GL path - use function pointers (no suffix)
|
|
if (glQueryCounter && glGetQueryObjectui64v) {
|
|
supported_ = true;
|
|
INFO_LOG(Log::G3D, "GLProfiler: Using GL_ARB_timer_query");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (supported_) {
|
|
// Pre-allocate query objects
|
|
queries_.resize(MAX_QUERY_COUNT);
|
|
glGenQueries(MAX_QUERY_COUNT, queries_.data());
|
|
CHECK_GL_ERROR_IF_DEBUG();
|
|
}
|
|
}
|
|
|
|
void GLProfiler::Shutdown() {
|
|
if (supported_ && !queries_.empty()) {
|
|
glDeleteQueries((GLsizei)queries_.size(), queries_.data());
|
|
queries_.clear();
|
|
}
|
|
supported_ = false;
|
|
scopes_.clear();
|
|
scopeStack_.clear();
|
|
}
|
|
|
|
void GLProfiler::BeginFrame() {
|
|
if (!supported_) {
|
|
return;
|
|
}
|
|
|
|
// Check if profiling is enabled
|
|
if (enabledPtr_ && !*enabledPtr_) {
|
|
scopes_.clear();
|
|
scopeStack_.clear();
|
|
numQueries_ = 0;
|
|
return;
|
|
}
|
|
|
|
// Check for disjoint operation (GPU frequency changed) - GLES only
|
|
if (gl_extensions.EXT_disjoint_timer_query) {
|
|
GLint disjoint = 0;
|
|
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
|
|
if (disjoint) {
|
|
// Results are invalid, just clear and start fresh
|
|
WARN_LOG(Log::G3D, "GLProfiler: GPU disjoint detected, timing results discarded");
|
|
scopes_.clear();
|
|
scopeStack_.clear();
|
|
numQueries_ = 0;
|
|
firstFrame_ = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Read results from previous frame (guaranteed complete now)
|
|
if (numQueries_ > 0 && !firstFrame_) {
|
|
static const char * const indent[4] = { "", " ", " ", " " };
|
|
|
|
if (!scopes_.empty()) {
|
|
DEBUG_LOG(Log::G3D, "OpenGL profiling events this frame:");
|
|
}
|
|
|
|
// Log results
|
|
for (auto &scope : scopes_) {
|
|
if (scope.endQueryId == -1) {
|
|
WARN_LOG(Log::G3D, "Unclosed scope: %s", scope.name);
|
|
continue;
|
|
}
|
|
|
|
GLuint64 startTime = 0, endTime = 0;
|
|
glGetQueryObjectui64v(queries_[scope.startQueryId], GL_QUERY_RESULT, &startTime);
|
|
glGetQueryObjectui64v(queries_[scope.endQueryId], GL_QUERY_RESULT, &endTime);
|
|
|
|
// Times are in nanoseconds, convert to milliseconds
|
|
double milliseconds = (double)(endTime - startTime) / 1000000.0;
|
|
|
|
DEBUG_LOG(Log::G3D, "%s%s (%0.3f ms)", indent[scope.level & 3], scope.name, milliseconds);
|
|
}
|
|
}
|
|
|
|
firstFrame_ = false;
|
|
scopes_.clear();
|
|
scopeStack_.clear();
|
|
numQueries_ = 0;
|
|
}
|
|
|
|
void GLProfiler::Begin(const char *fmt, ...) {
|
|
if (!supported_ || (enabledPtr_ && !*enabledPtr_) || numQueries_ >= MAX_QUERY_COUNT - 1) {
|
|
return;
|
|
}
|
|
|
|
GLProfilerScope scope;
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(scope.name, sizeof(scope.name), fmt, args);
|
|
va_end(args);
|
|
scope.startQueryId = numQueries_;
|
|
scope.endQueryId = -1;
|
|
scope.level = (int)scopeStack_.size();
|
|
|
|
scopeStack_.push_back(scopes_.size());
|
|
scopes_.push_back(scope);
|
|
|
|
glQueryCounter(queries_[numQueries_], GL_TIMESTAMP);
|
|
numQueries_++;
|
|
}
|
|
|
|
void GLProfiler::End() {
|
|
if (!supported_ || (enabledPtr_ && !*enabledPtr_) || numQueries_ >= MAX_QUERY_COUNT - 1) {
|
|
return;
|
|
}
|
|
|
|
if (scopeStack_.empty()) {
|
|
WARN_LOG(Log::G3D, "GLProfiler::End called without matching Begin");
|
|
return;
|
|
}
|
|
|
|
size_t scopeId = scopeStack_.back();
|
|
scopeStack_.pop_back();
|
|
|
|
GLProfilerScope &scope = scopes_[scopeId];
|
|
scope.endQueryId = numQueries_;
|
|
|
|
glQueryCounter(queries_[numQueries_], GL_TIMESTAMP);
|
|
numQueries_++;
|
|
}
|