summaryrefslogtreecommitdiff
path: root/retrace/daemon/glframe_metrics_intel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'retrace/daemon/glframe_metrics_intel.cpp')
-rw-r--r--retrace/daemon/glframe_metrics_intel.cpp523
1 files changed, 523 insertions, 0 deletions
diff --git a/retrace/daemon/glframe_metrics_intel.cpp b/retrace/daemon/glframe_metrics_intel.cpp
new file mode 100644
index 00000000..f199ff1b
--- /dev/null
+++ b/retrace/daemon/glframe_metrics_intel.cpp
@@ -0,0 +1,523 @@
+// Copyright (C) Intel Corp. 2015. 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 (including the
+// next paragraph) 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
+
+// **********************************************************************/
+// * Authors:
+// * Mark Janes <mark.a.janes@intel.com>
+// **********************************************************************/
+
+#include "glframe_metrics_intel.hpp"
+
+#include <GL/gl.h>
+#include <GL/glext.h>
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "glframe_glhelper.hpp"
+#include "glframe_logger.hpp"
+#include "glretrace.hpp"
+
+using glretrace::ExperimentId;
+using glretrace::GlFunctions;
+using glretrace::MetricId;
+using glretrace::NoAssign;
+using glretrace::NoCopy;
+using glretrace::OnFrameRetrace;
+using glretrace::PerfMetrics;
+using glretrace::PerfMetricsIntel;
+using glretrace::PerfMetricsContext;
+using glretrace::RenderId;
+using glretrace::SelectionId;
+using glretrace::GL;
+using glretrace::glretrace_delay;
+using glretrace::ID_PREFIX_MASK;
+
+namespace {
+
+struct MetricDescription {
+ MetricId id;
+ std::string name;
+ std::string description;
+ MetricDescription() {}
+ MetricDescription(MetricId i,
+ const std::string &n,
+ const std::string &d)
+ : id(i), name(n), description(d) {}
+};
+
+class PerfMetric : public NoCopy, NoAssign {
+ public:
+ PerfMetric(int query_id, int counter_num);
+ MetricId id() const;
+ const std::string &name() const;
+ const std::string &description() const;
+ float getMetric(const std::vector<unsigned char> &data) const;
+ private:
+ const int m_query_id, m_counter_num;
+ GLuint m_offset, m_data_size, m_type,
+ m_data_type;
+ std::string m_name, m_description;
+};
+
+
+class PerfMetricGroup : public NoCopy, NoAssign {
+ public:
+ explicit PerfMetricGroup(int query_id);
+ ~PerfMetricGroup();
+ const std::string &name() const { return m_query_name; }
+ void metrics(std::vector<MetricDescription> *m) const;
+ void begin(RenderId render);
+ void end(RenderId render);
+ void publish(MetricId metric, PerfMetricsIntel::MetricMap *m);
+
+ private:
+ std::string m_query_name;
+ const int m_query_id;
+ unsigned int m_data_size;
+ std::vector<unsigned char> m_data_buf;
+
+ std::map<MetricId, PerfMetric *> m_metrics;
+
+ // represent queries that have not produced results
+ std::map<RenderId, int> m_extant_query_handles;
+
+ // represent query handles that can be reused
+ std::vector<unsigned int> m_free_query_handles;
+};
+
+} // namespace
+
+namespace glretrace {
+
+class PerfMetricsContext : public NoCopy, NoAssign {
+ public:
+ explicit PerfMetricsContext(OnFrameRetrace *cb);
+ ~PerfMetricsContext();
+ int groupCount() const;
+ void selectMetric(MetricId metric);
+ void selectGroup(int index);
+ void begin(RenderId render);
+ void end();
+ void publish(PerfMetricsIntel::MetricMap *metrics);
+ private:
+ std::vector<PerfMetricGroup *> groups;
+ // indicates offset in groups of PerfMetricGroup reporting MetricId
+ std::map<MetricId, int> metric_map;
+ // indicates the group that will handle subsequent begin/end calls
+ PerfMetricGroup *current_group;
+ MetricId current_metric;
+ RenderId current_render;
+};
+
+} // namespace glretrace
+
+PerfMetricsContext::PerfMetricsContext(OnFrameRetrace *cb)
+ : current_group(NULL) {
+ GLuint query_id;
+ GLint count;
+ bool has_metrics = false;
+ GlFunctions::GetIntegerv(GL_NUM_EXTENSIONS, &count);
+ for (int i = 0; i < count; ++i) {
+ const GLubyte *name = GlFunctions::GetStringi(GL_EXTENSIONS, i);
+ if (strcmp((const char*)name, "GL_INTEL_performance_query") == 0)
+ has_metrics = true;
+ }
+ if (!has_metrics)
+ return;
+
+ GlFunctions::GetFirstPerfQueryIdINTEL(&query_id);
+ if (query_id == GLuint(-1))
+ return;
+
+ if (query_id == 0)
+ return;
+ std::vector<unsigned int> query_ids;
+ query_ids.push_back(query_id);
+
+ while (true) {
+ GlFunctions::GetNextPerfQueryIdINTEL(query_id, &query_id);
+ if (!query_id)
+ break;
+ query_ids.push_back(query_id);
+ }
+
+ std::map<std::string, MetricDescription> known_metrics;
+
+ std::vector<MetricDescription> metrics;
+ int group_index = 0;
+ for (auto i : query_ids) {
+ PerfMetricGroup *g = new PerfMetricGroup(i);
+ if (g->name() == "Compute Metrics Extended Gen9") {
+ // SKL metrics bug. Queries on this group crash.
+ delete g;
+ continue;
+ }
+
+ groups.push_back(g);
+ metrics.clear();
+ g->metrics(&metrics);
+ for (auto &d : metrics) {
+ if (known_metrics.find(d.name) == known_metrics.end()) {
+ known_metrics[d.name] = d;
+ metric_map[d.id] = group_index;
+ }
+ }
+ ++group_index;
+ }
+ std::vector<MetricId> ids;
+ std::vector<std::string> names;
+ std::vector<std::string> descriptions;
+ for (auto &i : known_metrics) {
+ names.push_back(i.second.name);
+ ids.push_back(i.second.id);
+ descriptions.push_back(i.second.description);
+ }
+ if (cb)
+ // only send metrics list on first context
+ cb->onMetricList(ids, names, descriptions);
+}
+
+PerfMetricsContext::~PerfMetricsContext() {
+ for (auto g : groups)
+ delete g;
+ groups.clear();
+}
+
+PerfMetricGroup::PerfMetricGroup(int query_id) : m_query_id(query_id) {
+ static GLint max_name_len = 0;
+ if (max_name_len == 0)
+ GlFunctions::GetIntegerv(GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL,
+ &max_name_len);
+
+ std::vector<GLchar> query_name(max_name_len);
+ unsigned int number_instances, capabilities_mask, number_counters;
+ GlFunctions::GetPerfQueryInfoINTEL(m_query_id,
+ query_name.size(), query_name.data(),
+ &m_data_size, &number_counters,
+ &number_instances, &capabilities_mask);
+ m_data_buf.resize(m_data_size);
+ m_query_name = query_name.data();
+ for (unsigned int counter_num = 1; counter_num <= number_counters;
+ ++counter_num) {
+ PerfMetric *p = new PerfMetric(m_query_id, counter_num);
+ m_metrics[p->id()] = p;
+ }
+}
+
+PerfMetricGroup::~PerfMetricGroup() {
+ for (auto free_query : m_free_query_handles) {
+ GlFunctions::DeletePerfQueryINTEL(free_query);
+ }
+ m_free_query_handles.clear();
+ assert(m_extant_query_handles.empty());
+ for (auto m : m_metrics)
+ delete m.second;
+ m_metrics.clear();
+}
+
+void
+PerfMetricGroup::metrics(std::vector<MetricDescription> *m) const {
+ for (auto &i : m_metrics) {
+ m->push_back(MetricDescription(i.first,
+ i.second->name(),
+ i.second->description()));
+ }
+}
+
+void
+PerfMetricGroup::begin(RenderId render) {
+ if (m_free_query_handles.empty()) {
+ GLuint query_handle;
+ GlFunctions::CreatePerfQueryINTEL(m_query_id, &query_handle);
+ m_free_query_handles.push_back(query_handle);
+ }
+ GLuint query_handle = m_free_query_handles.back();
+ m_free_query_handles.pop_back();
+
+ // When more than one process requests metrics concurrently,
+ // BeginPerfQueryINTEL fails.
+ int retry = 0;
+ GL::GetError();
+ while (true) {
+ GlFunctions::BeginPerfQueryINTEL(query_handle);
+ if (GL_NO_ERROR == GL::GetError())
+ break;
+ if (++retry > 20) {
+ GRLOG(glretrace::ERR, "failed to begin metrics query, aborting");
+ assert(false);
+ exit(-1);
+ }
+ GRLOG(glretrace::WARN, "failed to begin metrics query");
+ glretrace_delay(200);
+ }
+ m_extant_query_handles[render] = query_handle;
+}
+
+static const MetricId ALL_METRICS_IN_GROUP = MetricId(~ID_PREFIX_MASK);
+
+void
+PerfMetricGroup::publish(MetricId metric,
+ PerfMetricsIntel::MetricMap *out_metrics) {
+ const bool publish_all = (metric == ALL_METRICS_IN_GROUP);
+ for (auto extant_query : m_extant_query_handles) {
+ memset(m_data_buf.data(), 0, m_data_buf.size());
+ GLuint bytes_written;
+ GlFunctions::GetPerfQueryDataINTEL(extant_query.second,
+ GL_PERFQUERY_WAIT_INTEL,
+ m_data_size, m_data_buf.data(),
+ &bytes_written);
+ assert(bytes_written == m_data_size);
+
+ if (publish_all) {
+ for (auto desired_metric : m_metrics) {
+ MetricId met_id = desired_metric.first;
+ (*out_metrics)[met_id][extant_query.first] =
+ desired_metric.second->getMetric(m_data_buf);
+ }
+ } else {
+ (*out_metrics)[metric][extant_query.first] =
+ m_metrics[metric]->getMetric(m_data_buf);
+ }
+ m_free_query_handles.push_back(extant_query.second);
+ }
+ m_extant_query_handles.clear();
+ for (auto free_query : m_free_query_handles) {
+ GlFunctions::DeletePerfQueryINTEL(free_query);
+ }
+ m_free_query_handles.clear();
+}
+
+void
+PerfMetricGroup::end(RenderId render) {
+ if (m_extant_query_handles.find(render) != m_extant_query_handles.end())
+ GlFunctions::EndPerfQueryINTEL(m_extant_query_handles[render]);
+}
+
+
+PerfMetric::PerfMetric(int query_id,
+ int counter_num) : m_query_id(query_id),
+ m_counter_num(counter_num) {
+ static GLint max_name_len = 0, max_desc_len = 0;
+ if (max_name_len == 0)
+ GlFunctions::GetIntegerv(GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL,
+ &max_name_len);
+ if (max_desc_len == 0)
+ GlFunctions::GetIntegerv(GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL,
+ &max_desc_len);
+ std::vector<GLchar> counter_name(max_name_len);
+ std::vector<GLchar> counter_description(max_desc_len);
+ GLuint64 max_value;
+ GlFunctions::GetPerfCounterInfoINTEL(m_query_id, m_counter_num,
+ counter_name.size(), counter_name.data(),
+ counter_description.size(),
+ counter_description.data(),
+ &m_offset, &m_data_size, &m_type,
+ &m_data_type, &max_value);
+ m_name = counter_name.data();
+ m_description = counter_description.data();
+}
+
+MetricId
+PerfMetric::id() const {
+ return MetricId(m_query_id, m_counter_num);
+}
+
+const std::string &
+PerfMetric::name() const {
+ return m_name;
+}
+
+const std::string &
+PerfMetric::description() const {
+ return m_description;
+}
+
+float
+PerfMetric::getMetric(const std::vector<unsigned char> &data) const {
+ const unsigned char *p_value = data.data() + m_offset;
+ float fval;
+ switch (m_data_type) {
+ case GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL: {
+ assert(m_data_size == 4);
+ const uint32_t val = *reinterpret_cast<const uint32_t *>(p_value);
+ fval = static_cast<float>(val);
+ break;
+ }
+ case GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL: {
+ assert(m_data_size == 8);
+ const uint64_t val = *reinterpret_cast<const uint64_t *>(p_value);
+ fval = static_cast<float>(val);
+ break;
+ }
+ case GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL: {
+ assert(m_data_size == 4);
+ fval = *reinterpret_cast<const float *>(p_value);
+ break;
+ }
+ case GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL: {
+ assert(m_data_size == 8);
+ const double val = *reinterpret_cast<const double *>(p_value);
+ fval = static_cast<float>(val);
+ break;
+ }
+ case GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL: {
+ assert(m_data_size == 4);
+ const bool val = *reinterpret_cast<const bool*>(p_value);
+ fval = val ? 1.0 : 0.0;
+ break;
+ }
+ default:
+ assert(false);
+ }
+ return fval;
+}
+
+void
+PerfMetricsContext::selectMetric(MetricId metric) {
+ assert(metric_map.find(metric) != metric_map.end());
+ current_metric = metric;
+ current_group = groups[metric_map[metric]];
+}
+
+void
+PerfMetricsContext::publish(PerfMetricsIntel::MetricMap *metrics) {
+ current_group->publish(current_metric, metrics);
+}
+
+void
+PerfMetricsContext::begin(RenderId render) {
+ current_group->begin(render);
+ current_render = render;
+}
+
+void
+PerfMetricsContext::end() {
+ current_group->end(current_render);
+}
+
+int
+PerfMetricsContext::groupCount() const {
+ return groups.size();
+}
+
+void
+PerfMetricsContext::selectGroup(int index) {
+ current_group = groups[index];
+ current_metric = ALL_METRICS_IN_GROUP;
+}
+
+PerfMetricsIntel::PerfMetricsIntel(OnFrameRetrace *cb)
+ : m_current_group(0) {
+ Context *c = getCurrentContext();
+ m_current_context = new PerfMetricsContext(cb);
+ m_contexts[c] = m_current_context;
+}
+
+PerfMetricsIntel::~PerfMetricsIntel() {
+ for (auto i : m_contexts) {
+ delete i.second;
+ }
+ m_contexts.clear();
+}
+
+int
+PerfMetricsIntel::groupCount() const {
+ assert(!m_contexts.empty());
+ return m_contexts.begin()->second->groupCount();
+}
+
+void
+PerfMetricsIntel::selectMetric(MetricId metric) {
+ m_data.clear();
+ m_current_metric = metric;
+ for (auto i : m_contexts)
+ i.second->selectMetric(metric);
+}
+
+void
+PerfMetricsIntel::selectGroup(int index) {
+ m_current_group = index;
+ m_current_metric = ALL_METRICS_IN_GROUP;
+ for (auto i : m_contexts)
+ i.second->selectGroup(index);
+}
+
+void
+PerfMetricsIntel::begin(RenderId render) {
+ if (!m_current_context) {
+ beginContext();
+ }
+ m_current_context->begin(render);
+}
+
+void
+PerfMetricsIntel::end() {
+ if (m_current_context)
+ m_current_context->end();
+}
+
+void
+PerfMetricsIntel::publish(ExperimentId experimentCount,
+ SelectionId selectionCount,
+ OnFrameRetrace *callback) {
+ for (auto i : m_contexts)
+ i.second->publish(&m_data);
+
+ for (auto i : m_data) {
+ MetricSeries s;
+ s.metric = i.first;
+ s.data.resize(i.second.rbegin()->first.index() + 1);
+ for (auto datapoint : i.second)
+ s.data[datapoint.first.index()] = datapoint.second;
+ callback->onMetrics(s, experimentCount, selectionCount);
+ }
+ m_data.clear();
+}
+
+void
+PerfMetricsIntel::beginContext() {
+ Context *c = getCurrentContext();
+ auto entry = m_contexts.find(c);
+ if (entry != m_contexts.end()) {
+ m_current_context = entry->second;
+ } else {
+ // create a new metrics context
+ GRLOG(glretrace::WARN, "new context in frame");
+ m_current_context = new PerfMetricsContext(NULL);
+ m_contexts[c] = m_current_context;
+ }
+ m_current_context->selectGroup(m_current_group);
+ if (m_current_metric() &&
+ (m_current_metric != ALL_METRICS_IN_GROUP))
+ m_current_context->selectMetric(m_current_metric);
+}
+
+void
+PerfMetricsIntel::endContext() {
+ if (m_current_context) {
+ m_current_context->end();
+ m_current_context->publish(&m_data);
+ }
+ m_current_context = NULL;
+}