blob: 405121890d74ef46063e5b6fb460407119a4a2d9 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2015 Alexander Trukhin
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
**************************************************************************/
#include "metric_backend_amd_perfmon.hpp"
void Metric_AMD_perfmon::precache() {
GLenum type;
int length;
std::string name;
glGetPerfMonitorCounterInfoAMD(m_group, m_id, GL_COUNTER_TYPE_AMD, &type);
if (type == GL_UNSIGNED_INT) m_nType = CNT_NUM_UINT;
else if (type == GL_FLOAT || type == GL_PERCENTAGE_AMD) m_nType = CNT_NUM_FLOAT;
else if (type == GL_UNSIGNED_INT64_AMD) m_nType = CNT_NUM_UINT64;
else m_nType = CNT_NUM_UINT;
glGetPerfMonitorCounterStringAMD(m_group, m_id, 0, &length, nullptr);
name.resize(length);
glGetPerfMonitorCounterStringAMD(m_group, m_id, length, 0, &name[0]);
m_name = name;
m_precached = true;
}
unsigned Metric_AMD_perfmon::id() {
return m_id;
}
unsigned Metric_AMD_perfmon::groupId() {
return m_group;
}
std::string Metric_AMD_perfmon::name() {
if (!m_precached) precache();
return m_name;
}
std::string Metric_AMD_perfmon::description() {
return ""; // no description available
}
GLenum Metric_AMD_perfmon::size() {
GLenum type;
glGetPerfMonitorCounterInfoAMD(m_group, m_id, GL_COUNTER_TYPE_AMD, &type);
if (type == GL_UNSIGNED_INT) return sizeof(GLuint);
else if (type == GL_FLOAT || type == GL_PERCENTAGE_AMD) return sizeof(GLfloat);
else if (type == GL_UNSIGNED_INT64_AMD) return sizeof(uint64_t);
else return sizeof(GLuint);
}
MetricNumType Metric_AMD_perfmon::numType() {
if (!m_precached) precache();
return m_nType;
}
MetricType Metric_AMD_perfmon::type() {
GLenum type;
glGetPerfMonitorCounterInfoAMD(m_group, m_id, GL_COUNTER_TYPE_AMD, &type);
if ((type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT64_AMD) ||
(type == GL_FLOAT)) return CNT_TYPE_GENERIC;
else if (type == GL_PERCENTAGE_AMD) return CNT_TYPE_PERCENT;
else return CNT_TYPE_OTHER;
}
MetricBackend_AMD_perfmon::DataCollector::~DataCollector() {
for (auto &t1 : data) {
for (auto &t2 : t1) {
alloc.deallocate(t2, 1);
}
}
}
unsigned*
MetricBackend_AMD_perfmon::DataCollector::newDataBuffer(unsigned event,
size_t size)
{
// in case there is no data for previous events fill with nullptr
data[curPass].resize(event, nullptr);
data[curPass].push_back(alloc.allocate(size));
return data[curPass][event];
}
void MetricBackend_AMD_perfmon::DataCollector::endPass() {
curPass++;
data.push_back(mmapdeque<unsigned*>(alloc));
}
unsigned*
MetricBackend_AMD_perfmon::DataCollector::getDataBuffer(unsigned pass,
unsigned event)
{
if (event < data[pass].size()) {
return data[pass][event];
} else return nullptr;
}
MetricBackend_AMD_perfmon::MetricBackend_AMD_perfmon(glretrace::Context* context,
MmapAllocator<char> &alloc)
: numPasses(1), curPass(0), curEvent(0), collector(alloc) {
if (context->hasExtension("GL_AMD_performance_monitor")) {
supported = true;
} else {
supported = false;
}
}
bool MetricBackend_AMD_perfmon::isSupported() {
return supported;
}
void MetricBackend_AMD_perfmon::enumGroups(enumGroupsCallback callback,
void* userData)
{
std::vector<unsigned> groups;
int num_groups;
glGetPerfMonitorGroupsAMD(&num_groups, 0, nullptr);
groups.resize(num_groups);
glGetPerfMonitorGroupsAMD(nullptr, num_groups, &groups[0]);
for(unsigned &g : groups) {
callback(g, 0, userData);
}
}
void MetricBackend_AMD_perfmon::enumMetrics(unsigned group,
enumMetricsCallback callback,
void* userData)
{
std::vector<unsigned> metrics;
int num_metrics;
Metric_AMD_perfmon metric(0,0);
glGetPerfMonitorCountersAMD(group, &num_metrics, nullptr, 0, nullptr);
metrics.resize(num_metrics);
glGetPerfMonitorCountersAMD(group, nullptr, nullptr, num_metrics, &metrics[0]);
for(unsigned &c : metrics) {
metric = Metric_AMD_perfmon(group, c);
callback(&metric, 0, userData);
}
}
std::unique_ptr<Metric>
MetricBackend_AMD_perfmon::getMetricById(unsigned groupId, unsigned metricId)
{
std::unique_ptr<Metric> p(new Metric_AMD_perfmon(groupId, metricId));
return p;
}
void MetricBackend_AMD_perfmon::populateLookupGroups(unsigned group,
int error,
void* userData)
{
reinterpret_cast<MetricBackend_AMD_perfmon*>(userData)->enumMetrics(group,
populateLookupMetrics);
}
void MetricBackend_AMD_perfmon::populateLookupMetrics(Metric* metric,
int error,
void* userData)
{
nameLookup[metric->name()] = std::make_pair(metric->groupId(),
metric->id());
}
std::unique_ptr<Metric>
MetricBackend_AMD_perfmon::getMetricByName(std::string metricName)
{
if (nameLookup.empty()) {
enumGroups(populateLookupGroups, this);
}
if (nameLookup.count(metricName) > 0) {
std::unique_ptr<Metric> p(new Metric_AMD_perfmon(nameLookup[metricName].first,
nameLookup[metricName].second));
return p;
}
else return nullptr;
}
std::string MetricBackend_AMD_perfmon::getGroupName(unsigned group) {
int length;
std::string name;
glGetPerfMonitorGroupStringAMD(group, 0, &length, nullptr);
name.resize(length);
glGetPerfMonitorGroupStringAMD(group, length, 0, &name[0]);
return name;
}
int MetricBackend_AMD_perfmon::enableMetric(Metric* metric_, QueryBoundary pollingRule) {
unsigned id = metric_->id();
unsigned gid = metric_->groupId();
unsigned monitor;
// profiling only draw calls
if (pollingRule == QUERY_BOUNDARY_CALL) return 1;
// check that Metric is valid metric
glGenPerfMonitorsAMD(1, &monitor);
glGetError();
glSelectPerfMonitorCountersAMD(monitor, 1, gid, 1, &id);
GLenum err = glGetError();
glDeletePerfMonitorsAMD(1, &monitor);
if (err == GL_INVALID_VALUE) {
return 1;
}
Metric_AMD_perfmon metric(gid, id);
metric.numType(); // triggers metric vars precache (in case context changes)
metrics[pollingRule].push_back(metric);
return 0;
}
bool
MetricBackend_AMD_perfmon::testMetrics(std::vector<Metric_AMD_perfmon>* metrics) {
unsigned monitor;
unsigned id;
glGenPerfMonitorsAMD(1, &monitor);
for (Metric_AMD_perfmon &c : *metrics) {
id = c.id();
glSelectPerfMonitorCountersAMD(monitor, 1, c.groupId(), 1, &id);
}
glGetError();
glBeginPerfMonitorAMD(monitor);
GLenum err = glGetError();
glEndPerfMonitorAMD(monitor);
glDeletePerfMonitorsAMD(1, &monitor);
if (err == GL_INVALID_OPERATION) {
return 0;
}
return 1;
}
void MetricBackend_AMD_perfmon::generatePassesBoundary(QueryBoundary boundary) {
std::vector<Metric_AMD_perfmon> copyMetrics(metrics[boundary]);
std::vector<Metric_AMD_perfmon> newPass;
while (!copyMetrics.empty()) {
std::vector<Metric_AMD_perfmon>::iterator it = copyMetrics.begin();
while (it != copyMetrics.end()) {
newPass.push_back(*it);
if (!testMetrics(&newPass)) {
newPass.pop_back();
break;
}
it = copyMetrics.erase(it);
}
passes.push_back(newPass);
newPass.clear();
}
}
unsigned MetricBackend_AMD_perfmon::generatePasses() {
generatePassesBoundary(QUERY_BOUNDARY_FRAME);
numFramePasses = passes.size();
generatePassesBoundary(QUERY_BOUNDARY_DRAWCALL);
nameLookup.clear(); // no need in it after all metrics are set up
numPasses = passes.size();
return passes.size();
}
void MetricBackend_AMD_perfmon::beginPass() {
if (!numPasses) return;
/* First process per-frame passes, then per-call passes */
if (curPass < numFramePasses) {
perFrame = true;
} else {
perFrame = false;
}
/* Generate monitor */
glGenPerfMonitorsAMD(NUM_MONITORS, monitors);
for (Metric_AMD_perfmon &c : passes[curPass]) {
unsigned id = c.id();
for (auto & monitor : monitors) {
glSelectPerfMonitorCountersAMD(monitor, 1, c.groupId(), 1, &id);
}
}
curMonitor = 0;
firstRound = true;
curEvent = 0;
supported = true; // can change if context is switched, so revert back
}
void MetricBackend_AMD_perfmon::endPass() {
if (supported && numPasses) {
for (unsigned k = 0; k < curMonitor; k++) {
freeMonitor(k);
}
glDeletePerfMonitorsAMD(NUM_MONITORS, monitors);
}
curPass++;
collector.endPass();
}
void MetricBackend_AMD_perfmon::pausePass() {
if (!supported || !numPasses) return;
// clear all queries and monitors
// ignore data from the query in progress
if (queryInProgress) {
glEndPerfMonitorAMD(monitors[curMonitor]);
curEvent++;
queryInProgress = false;
}
for (unsigned k = 0; k < curMonitor; k++) {
freeMonitor(k);
}
glDeletePerfMonitorsAMD(NUM_MONITORS, monitors);
}
void MetricBackend_AMD_perfmon::continuePass() {
// here new context might be used
// better to check if it supports AMD_perfmon extension
glretrace::Context* context = glretrace::getCurrentContext();
if (context && context->hasExtension("GL_AMD_performance_monitor")) {
supported = true;
} else {
supported = false;
}
if (supported && numPasses) {
// call begin pass and save/restore event id
unsigned tempId = curEvent;
beginPass();
curEvent = tempId;
}
}
void MetricBackend_AMD_perfmon::beginQuery(QueryBoundary boundary) {
if (!supported || !numPasses) return;
if (boundary == QUERY_BOUNDARY_CALL) return;
if ((boundary == QUERY_BOUNDARY_FRAME) && !perFrame) return;
if ((boundary == QUERY_BOUNDARY_DRAWCALL) && perFrame) return;
curMonitor %= NUM_MONITORS;
if (!firstRound) freeMonitor(curMonitor); // get existing data
monitorEvent[curMonitor] = curEvent; // save monitored event
glBeginPerfMonitorAMD(monitors[curMonitor]);
queryInProgress = true;
}
void MetricBackend_AMD_perfmon::endQuery(QueryBoundary boundary) {
if (!queryInProgress) return;
if (!supported || !numPasses) return;
if (boundary == QUERY_BOUNDARY_CALL) return;
if ((boundary == QUERY_BOUNDARY_FRAME) && !perFrame) return;
if ((boundary == QUERY_BOUNDARY_DRAWCALL) && perFrame) return;
curEvent++;
glEndPerfMonitorAMD(monitors[curMonitor]);
curMonitor++;
if (curMonitor == NUM_MONITORS) firstRound = 0;
queryInProgress = false;
}
void MetricBackend_AMD_perfmon::freeMonitor(unsigned monitorId) {
unsigned monitor = monitors[monitorId];
GLuint dataAvail = 0;
GLuint size;
glFlush();
while (!dataAvail) {
glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AVAILABLE_AMD,
sizeof(GLuint), &dataAvail, nullptr);
}
glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_SIZE_AMD,
sizeof(GLuint), &size, nullptr);
// collect data
unsigned* buf = collector.newDataBuffer(monitorEvent[monitorId],
size/sizeof(unsigned));
glGetPerfMonitorCounterDataAMD(monitor, GL_PERFMON_RESULT_AMD, size, buf, nullptr);
/* populate metricOffsets */
if (metricOffsets.size() < curPass + 1) {
std::map<std::pair<unsigned, unsigned>, unsigned> pairOffsets;
unsigned offset = 0;
unsigned id, gid;
for (int k = 0; k < passes[curPass].size(); k++) {
gid = buf[offset++];
id = buf[offset++];
pairOffsets[std::make_pair(gid, id)] = offset;
Metric_AMD_perfmon metric(gid, id);
offset += metric.size() / sizeof(unsigned);
}
// translate to existing metrics in passes variable
std::map<Metric_AMD_perfmon*, unsigned> temp;
for (auto &m : passes[curPass]) {
id = m.id();
gid = m.groupId();
temp[&m] = pairOffsets[std::make_pair(gid, id)];
}
metricOffsets.push_back(std::move(temp));
}
}
void
MetricBackend_AMD_perfmon::enumDataQueryId(unsigned id,
enumDataCallback callback,
QueryBoundary boundary,
void* userData)
{
/* Determine passes to return depending on the boundary */
if (boundary == QUERY_BOUNDARY_CALL) return;
unsigned j = 0;
unsigned nPasses = numFramePasses;
if (boundary == QUERY_BOUNDARY_DRAWCALL) {
j = numFramePasses;
nPasses = numPasses;
}
/* enum passes */
for (; j < nPasses; j++) {
unsigned* buf = collector.getDataBuffer(j, id);
for (auto &m : passes[j]) {
void* data = (buf) ? &buf[metricOffsets[j][&m]] : nullptr;
callback(&m, id, data, 0, userData);
}
}
}
unsigned MetricBackend_AMD_perfmon::getNumPasses() {
return numPasses;
}
MetricBackend_AMD_perfmon&
MetricBackend_AMD_perfmon::getInstance(glretrace::Context* context,
MmapAllocator<char> &alloc) {
static MetricBackend_AMD_perfmon backend(context, alloc);
return backend;
}
std::map<std::string, std::pair<unsigned, unsigned>> MetricBackend_AMD_perfmon::nameLookup;