#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"
#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
#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
void GLProfiler::Init() {
supported_ = false;
firstFrame_ = true;
numQueries_ = 0;
scopes_.clear();
scopeStack_.clear();
if (gl_extensions.EXT_disjoint_timer_query) {
if (glQueryCounter && glGetQueryObjectui64v) {
supported_ = true;
INFO_LOG(Log::G3D, "GLProfiler: Using GL_EXT_disjoint_timer_query");
}
} else if (gl_extensions.ARB_timer_query) {
if (glQueryCounter && glGetQueryObjectui64v) {
supported_ = true;
INFO_LOG(Log::G3D, "GLProfiler: Using GL_ARB_timer_query");
}
}
if (supported_) {
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;
}
if (enabledPtr_ && !*enabledPtr_) {
scopes_.clear();
scopeStack_.clear();
numQueries_ = 0;
return;
}
if (gl_extensions.EXT_disjoint_timer_query) {
GLint disjoint = 0;
glGetIntegerv(GL_GPU_DISJOINT_EXT, &disjoint);
if (disjoint) {
WARN_LOG(Log::G3D, "GLProfiler: GPU disjoint detected, timing results discarded");
scopes_.clear();
scopeStack_.clear();
numQueries_ = 0;
firstFrame_ = true;
return;
}
}
if (numQueries_ > 0 && !firstFrame_) {
static const char * const indent[4] = { "", " ", " ", " " };
if (!scopes_.empty()) {
INFO_LOG(Log::G3D, "OpenGL profiling events this frame:");
}
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);
double milliseconds = (double)(endTime - startTime) / 1000000.0;
INFO_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_++;
}