/************************************************************************** * * 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_intel_perfquery.hpp" void Metric_INTEL_perfquery::precache() { unsigned offset; glGetPerfCounterInfoINTEL(m_group, m_id, 0, nullptr, 0, nullptr, &offset, nullptr, nullptr, nullptr, nullptr); this->m_offset = offset; GLenum type; glGetPerfCounterInfoINTEL(m_group, m_id, 0, nullptr, 0, nullptr, nullptr, nullptr, nullptr, &type, nullptr); if (type == GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL) m_nType = CNT_NUM_UINT; else if (type == GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL) m_nType = CNT_NUM_FLOAT; else if (type == GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL) m_nType = CNT_NUM_DOUBLE; else if (type == GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL) m_nType = CNT_NUM_BOOL; else if (type == GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL) m_nType = CNT_NUM_UINT64; else m_nType = CNT_NUM_UINT; char name[INTEL_NAME_LENGTH]; glGetPerfCounterInfoINTEL(m_group, m_id, INTEL_NAME_LENGTH, name, 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); m_name = std::string(name); m_precached = true; } unsigned Metric_INTEL_perfquery::id() { return m_id; } unsigned Metric_INTEL_perfquery::groupId() { return m_group; } std::string Metric_INTEL_perfquery::name() { if (!m_precached) precache(); return m_name; } std::string Metric_INTEL_perfquery::description() { char desc[INTEL_DESC_LENGTH]; glGetPerfCounterInfoINTEL(m_group, m_id, 0, nullptr, INTEL_DESC_LENGTH, desc, nullptr, nullptr, nullptr, nullptr, nullptr); return std::string(desc); } unsigned Metric_INTEL_perfquery::offset() { if (!m_precached) precache(); return m_offset; } MetricNumType Metric_INTEL_perfquery::numType() { if (!m_precached) precache(); return m_nType; } MetricType Metric_INTEL_perfquery::type() { GLenum type; glGetPerfCounterInfoINTEL(m_group, m_id, 0, nullptr, 0, nullptr, nullptr, nullptr, &type, nullptr, nullptr); if (type == GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL) return CNT_TYPE_TIMESTAMP; else if (type == GL_PERFQUERY_COUNTER_EVENT_INTEL) return CNT_TYPE_NUM; else if (type == GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL || type == GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL) return CNT_TYPE_DURATION; else if (type == GL_PERFQUERY_COUNTER_RAW_INTEL) return CNT_TYPE_GENERIC; else if (type == GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL) return CNT_TYPE_GENERIC; else return CNT_TYPE_OTHER; } MetricBackend_INTEL_perfquery::DataCollector::~DataCollector() { for (auto &t1 : data) { for (auto &t2 : t1) { alloc.deallocate(t2, 1); } } } unsigned char* MetricBackend_INTEL_perfquery::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_INTEL_perfquery::DataCollector::endPass() { curPass++; data.push_back(mmapdeque(alloc)); } unsigned char* MetricBackend_INTEL_perfquery::DataCollector::getDataBuffer(unsigned pass, unsigned event) { if (event < data[pass].size()) { return data[pass][event]; } else return nullptr; } MetricBackend_INTEL_perfquery::MetricBackend_INTEL_perfquery(glretrace::Context* context, MmapAllocator &alloc) : numPasses(1), curPass(0), curEvent(0), collector(alloc) { if (context->hasExtension("GL_INTEL_performance_query")) { supported = true; } else { supported = false; } } bool MetricBackend_INTEL_perfquery::isSupported() { return supported; } void MetricBackend_INTEL_perfquery::enumGroups(enumGroupsCallback callback, void* userData) { unsigned gid; glGetFirstPerfQueryIdINTEL(&gid); while (gid) { callback(gid, 0, userData); glGetNextPerfQueryIdINTEL(gid, &gid); } } void MetricBackend_INTEL_perfquery::enumMetrics(unsigned group, enumMetricsCallback callback, void* userData) { unsigned numMetrics; glGetPerfQueryInfoINTEL(group, 0, nullptr, nullptr, &numMetrics, nullptr, nullptr); for (int i = 1; i <= numMetrics; i++) { Metric_INTEL_perfquery metric = Metric_INTEL_perfquery(group, i); callback(&metric, 0, userData); } } std::unique_ptr MetricBackend_INTEL_perfquery::getMetricById(unsigned groupId, unsigned metricId) { std::unique_ptr p(new Metric_INTEL_perfquery(groupId, metricId)); return p; } void MetricBackend_INTEL_perfquery::populateLookupGroups(unsigned group, int error, void* userData) { reinterpret_cast(userData)->enumMetrics(group, populateLookupMetrics); } void MetricBackend_INTEL_perfquery::populateLookupMetrics(Metric* metric, int error, void* userData) { nameLookup[metric->name()] = std::make_pair(metric->groupId(), metric->id()); } std::unique_ptr MetricBackend_INTEL_perfquery::getMetricByName(std::string metricName) { if (nameLookup.empty()) { enumGroups(populateLookupGroups, this); } if (nameLookup.count(metricName) > 0) { std::unique_ptr p(new Metric_INTEL_perfquery(nameLookup[metricName].first, nameLookup[metricName].second)); return p; } else return nullptr; } std::string MetricBackend_INTEL_perfquery::getGroupName(unsigned group) { char name[INTEL_NAME_LENGTH]; glGetPerfQueryInfoINTEL(group, INTEL_NAME_LENGTH, name, nullptr, nullptr, nullptr, nullptr); return std::string(name); } int MetricBackend_INTEL_perfquery::enableMetric(Metric* metric_, QueryBoundary pollingRule) { if (pollingRule == QUERY_BOUNDARY_CALL) return 1; unsigned id = metric_->id(); unsigned gid = metric_->groupId(); unsigned numCounters; /* check that counter id is in valid range and group exists */ glGetError(); glGetPerfQueryInfoINTEL(gid, 0, nullptr, nullptr, &numCounters, nullptr, nullptr); GLenum err = glGetError(); if (gid >= numCounters || err == GL_INVALID_VALUE) { return 1; } Metric_INTEL_perfquery metric(gid, id); metric.offset(); // triggers metric vars precache (in case context changes) passes[pollingRule][gid].push_back(metric); return 0; } unsigned MetricBackend_INTEL_perfquery::generatePasses() { /* begin with passes that profile frames */ perFrame = true; curQueryMetrics = passes[QUERY_BOUNDARY_FRAME].begin(); numFramePasses = passes[QUERY_BOUNDARY_FRAME].size(); numPasses = numFramePasses + passes[QUERY_BOUNDARY_DRAWCALL].size(); nameLookup.clear(); // no need in it after all metrics are set up return numPasses; } void MetricBackend_INTEL_perfquery::beginPass() { if (!numPasses || curQueryMetrics == passes[QUERY_BOUNDARY_DRAWCALL].end()) return; /* advance to draw calls after frames */ if (curQueryMetrics == passes[QUERY_BOUNDARY_FRAME].end()) { perFrame = false; curQueryMetrics = passes[QUERY_BOUNDARY_DRAWCALL].begin(); } glCreatePerfQueryINTEL(curQueryMetrics->first, &curQuery); curEvent = 0; supported = true; // can change if context is switched, so revert back } void MetricBackend_INTEL_perfquery::endPass() { if (supported && numPasses) { glDeletePerfQueryINTEL(curQuery); } curPass++; curQueryMetrics++; collector.endPass(); } void MetricBackend_INTEL_perfquery::pausePass() { if (!supported || !numPasses) return; // end query // ignore data from the query in progress if (queryInProgress) { glEndPerfQueryINTEL(curQuery); curEvent++; queryInProgress = false; } glDeletePerfQueryINTEL(curQuery); } void MetricBackend_INTEL_perfquery::continuePass() { // here new context might be used // better to check if it supports INTEL_perfquery extension glretrace::Context* context = glretrace::getCurrentContext(); if (context && context->hasExtension("GL_INTEL_performance_query")) { supported = true; } else { supported = false; } if (supported && numPasses) { // call begin pass and save/restore event id unsigned tempId = curEvent; beginPass(); curEvent = tempId; } } void MetricBackend_INTEL_perfquery::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; glBeginPerfQueryINTEL(curQuery); queryInProgress = true; } void MetricBackend_INTEL_perfquery::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; glEndPerfQueryINTEL(curQuery); freeQuery(curEvent++); queryInProgress = false; } void MetricBackend_INTEL_perfquery::freeQuery(unsigned event) { GLuint size; GLuint bWritten; glGetPerfQueryInfoINTEL(curQueryMetrics->first, 0, nullptr, &size, nullptr, nullptr, nullptr); unsigned char* data = collector.newDataBuffer(event, size); glFlush(); glGetPerfQueryDataINTEL(curQuery, GL_PERFQUERY_WAIT_INTEL, size, data, &bWritten); // bWritten != size -> should generate error TODO } void MetricBackend_INTEL_perfquery::enumDataQueryId(unsigned id, enumDataCallback callback, QueryBoundary boundary, void* userData) { /* Determine passes to return depending on the boundary */ if (boundary == QUERY_BOUNDARY_CALL) return; auto queryIt = passes[QUERY_BOUNDARY_FRAME].begin(); unsigned j = 0; unsigned nPasses = numFramePasses; if (boundary == QUERY_BOUNDARY_DRAWCALL) { queryIt = passes[QUERY_BOUNDARY_DRAWCALL].begin(); j = numFramePasses; nPasses = numPasses; } for (; j < nPasses; j++) { unsigned char* buf = collector.getDataBuffer(j, id); for (auto &k : queryIt->second) { if (buf) { callback(&k, id, &buf[k.offset()], 0, userData); } else { // No data buffer (in case event #id is not a draw call) callback(&k, id, nullptr, 0, userData); } } queryIt++; } } unsigned MetricBackend_INTEL_perfquery::getNumPasses() { return numPasses; } MetricBackend_INTEL_perfquery& MetricBackend_INTEL_perfquery::getInstance(glretrace::Context* context, MmapAllocator &alloc) { static MetricBackend_INTEL_perfquery backend(context, alloc); return backend; } std::map> MetricBackend_INTEL_perfquery::nameLookup;