| /************************************************************************** |
| * |
| * 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; |