[Nouveau] [RFC 3/3] st/perfkit: add a state tracker for NVIDIA PerfKit

Samuel Pitoiset samuel.pitoiset at gmail.com
Tue Jun 2 13:15:04 PDT 2015


This state tracker implements NVIDIA PerfKit 3.0.1 which is currently
only available on Windows 7+ for desktop graphics cards. A Linux/Android
implementation is provided by NVIDIA but only for NVIDIA Tegra K1.

This Gallium state tracker is loosely based on the VDPAU tracker and since
it uses the pipe_query interface, other drivers which expose performance
counters should be also able to expose them easily through PerfKit.

Unlike GL_AMD_performance_monitor, NVIDIA PerfKit allows to monitor
multi-passes events and the API supports different types of performance
counters (GPU, CPU, OpenGL).

Currently, only MP counters for NVC0:NVE4 Nouveau drivers are exposed.

To be able to use NVIDIA PerfKit from your OpenGL applications, you will
need to install the wrapper library which includes the headers of the API.

You can download this library from:
http://cgit.freedesktop.org/~hakzsam/libperfkit/

Signed-off-by: Samuel Pitoiset <samuel.pitoiset at gmail.com>
---
 configure.ac                                       |  30 ++
 src/gallium/Makefile.am                            |   4 +
 src/gallium/state_trackers/perfkit/Makefile.am     |  41 ++
 .../state_trackers/perfkit/Makefile.sources        |   8 +
 src/gallium/state_trackers/perfkit/context.c       | 186 ++++++++
 src/gallium/state_trackers/perfkit/counter.c       | 522 +++++++++++++++++++++
 src/gallium/state_trackers/perfkit/device.c        |  91 ++++
 src/gallium/state_trackers/perfkit/entrypoint.c    |  78 +++
 src/gallium/state_trackers/perfkit/monitoring.c    | 360 ++++++++++++++
 .../state_trackers/perfkit/perfkit_private.h       | 145 ++++++
 src/gallium/state_trackers/perfkit/util.c          |  56 +++
 src/gallium/targets/dri-perfkit.dyn                |   3 +
 src/gallium/targets/perfkit/Makefile.am            | 123 +++++
 src/gallium/targets/perfkit/perfkit.sym            |   7 +
 src/gallium/targets/perfkit/target.c               |   1 +
 15 files changed, 1655 insertions(+)
 create mode 100644 src/gallium/state_trackers/perfkit/Makefile.am
 create mode 100644 src/gallium/state_trackers/perfkit/Makefile.sources
 create mode 100644 src/gallium/state_trackers/perfkit/context.c
 create mode 100644 src/gallium/state_trackers/perfkit/counter.c
 create mode 100644 src/gallium/state_trackers/perfkit/device.c
 create mode 100644 src/gallium/state_trackers/perfkit/entrypoint.c
 create mode 100644 src/gallium/state_trackers/perfkit/monitoring.c
 create mode 100644 src/gallium/state_trackers/perfkit/perfkit_private.h
 create mode 100644 src/gallium/state_trackers/perfkit/util.c
 create mode 100644 src/gallium/targets/dri-perfkit.dyn
 create mode 100644 src/gallium/targets/perfkit/Makefile.am
 create mode 100644 src/gallium/targets/perfkit/perfkit.sym
 create mode 100644 src/gallium/targets/perfkit/target.c

diff --git a/configure.ac b/configure.ac
index d32aa24..6c7aa03 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,7 @@ LIBUDEV_REQUIRED=151
 GLPROTO_REQUIRED=1.4.14
 LIBOMXIL_BELLAGIO_REQUIRED=0.0
 LIBVA_REQUIRED=0.35.0
+PERFKIT_REQUIRED=3.0
 VDPAU_REQUIRED=0.4.1
 WAYLAND_REQUIRED=1.2.0
 XCB_REQUIRED=1.9.3
@@ -790,6 +791,11 @@ AC_ARG_ENABLE([vdpau],
          [enable vdpau library @<:@default=auto@:>@])],
    [enable_vdpau="$enableval"],
    [enable_vdpau=auto])
+AC_ARG_ENABLE([perfkit],
+   [AS_HELP_STRING([--enable-perfkit],
+         [enable perfkit library @<:@default=auto@:>@])],
+   [enable_perfkit="$enableval"],
+   [enable_perfkit=auto])
 AC_ARG_ENABLE([omx],
    [AS_HELP_STRING([--enable-omx],
          [enable OpenMAX library @<:@default=disabled@:>@])],
@@ -856,6 +862,7 @@ if test "x$enable_opengl" = xno -a \
         "x$enable_xa" = xno -a \
         "x$enable_xvmc" = xno -a \
         "x$enable_vdpau" = xno -a \
+        "x$enable_perfkit" = xno -a \
         "x$enable_omx" = xno -a \
         "x$enable_va" = xno -a \
         "x$enable_opencl" = xno; then
@@ -1584,6 +1591,10 @@ if test -n "$with_gallium_drivers" -a "x$with_gallium_drivers" != xswrast; then
 	PKG_CHECK_EXISTS([vdpau >= $VDPAU_REQUIRED], [enable_vdpau=yes], [enable_vdpau=no])
     fi
 
+    if test "x$enable_perfkit" = xauto; then
+	PKG_CHECK_EXISTS([perfkit], [enable_perfkit=yes], [enable_perfkit=no])
+    fi
+
     if test "x$enable_omx" = xauto; then
 	PKG_CHECK_EXISTS([libomxil-bellagio >= $LIBOMXIL_BELLAGIO_REQUIRED], [enable_omx=yes], [enable_omx=no])
     fi
@@ -1623,6 +1634,12 @@ if test "x$enable_vdpau" = xyes; then
 fi
 AM_CONDITIONAL(HAVE_ST_VDPAU, test "x$enable_vdpau" = xyes)
 
+if test "x$enable_perfkit" = xyes; then
+    PKG_CHECK_MODULES([PERFKIT], [perfkit >= $PERFKIT_REQUIRED])
+    enable_gallium_loader=$enable_shared_pipe_drivers
+fi
+AM_CONDITIONAL(HAVE_ST_PERFKIT, test "x$enable_perfkit" = xyes)
+
 if test "x$enable_omx" = xyes; then
     PKG_CHECK_MODULES([OMX], [libomxil-bellagio >= $LIBOMXIL_BELLAGIO_REQUIRED])
     enable_gallium_loader=$enable_shared_pipe_drivers
@@ -1979,6 +1996,14 @@ AC_ARG_WITH([vdpau-libdir],
     [VDPAU_LIB_INSTALL_DIR='${libdir}/vdpau'])
 AC_SUBST([VDPAU_LIB_INSTALL_DIR])
 
+dnl Directory for PERFKIT libs
+AC_ARG_WITH([perfkit-libdir],
+    [AS_HELP_STRING([--with-perfkit-libdir=DIR],
+        [directory for the PERFKIT libraries @<:@default=${libdir}/perfkit@:>@])],
+    [PERFKIT_LIB_INSTALL_DIR="$withval"],
+    [PERFKIT_LIB_INSTALL_DIR='${libdir}/perfkit'])
+AC_SUBST([PERFKIT_LIB_INSTALL_DIR])
+
 dnl Directory for OMX libs
 
 AC_ARG_WITH([omx-libdir],
@@ -2291,6 +2316,9 @@ AC_SUBST([NINE_VERSION], "$NINE_MAJOR.$NINE_MINOR.$NINE_TINY")
 AC_SUBST([VDPAU_MAJOR], 1)
 AC_SUBST([VDPAU_MINOR], 0)
 
+AC_SUBST([PERFKIT_MAJOR], 1)
+AC_SUBST([PERFKIT_MINOR], 0)
+
 VA_MAJOR=`$PKG_CONFIG --modversion libva | $SED -n 's/\([[^\.]]*\)\..*$/\1/p'`
 VA_MINOR=`$PKG_CONFIG --modversion libva | $SED -n 's/.*\.\(.*\)\..*$/\1/p'`
 AC_SUBST([VA_MAJOR], $VA_MAJOR)
@@ -2357,6 +2385,7 @@ AC_CONFIG_FILES([Makefile
 		src/gallium/state_trackers/nine/Makefile
 		src/gallium/state_trackers/omx/Makefile
 		src/gallium/state_trackers/osmesa/Makefile
+		src/gallium/state_trackers/perfkit/Makefile
 		src/gallium/state_trackers/va/Makefile
 		src/gallium/state_trackers/vdpau/Makefile
 		src/gallium/state_trackers/xa/Makefile
@@ -2369,6 +2398,7 @@ AC_CONFIG_FILES([Makefile
 		src/gallium/targets/opencl/Makefile
 		src/gallium/targets/osmesa/Makefile
 		src/gallium/targets/osmesa/osmesa.pc
+		src/gallium/targets/perfkit/Makefile
 		src/gallium/targets/pipe-loader/Makefile
 		src/gallium/targets/va/Makefile
 		src/gallium/targets/vdpau/Makefile
diff --git a/src/gallium/Makefile.am b/src/gallium/Makefile.am
index ede6e21..b3523d7 100644
--- a/src/gallium/Makefile.am
+++ b/src/gallium/Makefile.am
@@ -141,6 +141,10 @@ if HAVE_ST_VA
 SUBDIRS += state_trackers/va targets/va
 endif
 
+if HAVE_ST_PERFKIT
+SUBDIRS += state_trackers/perfkit targets/perfkit
+endif
+
 if HAVE_ST_VDPAU
 SUBDIRS += state_trackers/vdpau targets/vdpau
 endif
diff --git a/src/gallium/state_trackers/perfkit/Makefile.am b/src/gallium/state_trackers/perfkit/Makefile.am
new file mode 100644
index 0000000..2885a34
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/Makefile.am
@@ -0,0 +1,41 @@
+# Copyright © 2015 Samuel Pitoiset
+#
+# 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 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 Makefile.sources
+include $(top_srcdir)/src/gallium/Automake.inc
+
+PERFKIT_MAJOR = 1
+PERFKIT_MINOR = 0
+
+AM_CFLAGS = \
+	$(GALLIUM_CFLAGS) \
+	$(VISIBILITY_CFLAGS) \
+	$(VL_CFLAGS) \
+	$(PERFKIT_CFLAGS)
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-DVER_MAJOR=$(PERFKIT_MAJOR) \
+	-DVER_MINOR=$(PERFKIT_MINOR)
+
+noinst_LTLIBRARIES = libperfkittracker.la
+
+libperfkittracker_la_SOURCES = $(C_SOURCES)
diff --git a/src/gallium/state_trackers/perfkit/Makefile.sources b/src/gallium/state_trackers/perfkit/Makefile.sources
new file mode 100644
index 0000000..3595974
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/Makefile.sources
@@ -0,0 +1,8 @@
+C_SOURCES := \
+	context.c \
+	counter.c \
+	device.c \
+	entrypoint.c \
+	monitoring.c \
+	perfkit_private.h \
+	util.c
diff --git a/src/gallium/state_trackers/perfkit/context.c b/src/gallium/state_trackers/perfkit/context.c
new file mode 100644
index 0000000..7d80173
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/context.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * 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 "pipe/p_context.h"
+#include "pipe/p_screen.h"
+
+#include "util/u_memory.h"
+
+#include "perfkit_private.h"
+
+/**
+ * Query the list of available counters for this device/context.
+ */
+static NVPMRESULT
+perfkit_query_counters(struct perfkit_device *dev, struct perfkit_context *ctx)
+{
+   struct pipe_driver_query_info info;
+   struct pipe_screen *pscreen;
+   unsigned num_queries, i;
+
+   pscreen = dev->vscreen->pscreen;
+   num_queries = pscreen->get_driver_query_info(pscreen, 0, NULL);
+   for (i = 0; i < num_queries; i++) {
+      struct perfkit_counter *ctr;
+      if (!pscreen->get_driver_query_info(pscreen, i, &info))
+         continue;
+
+      ctr = CALLOC_STRUCT(perfkit_counter);
+      if (!ctr)
+         return NVPM_ERROR_OUT_OF_MEMORY;
+
+      ctr->name = malloc(strlen(info.name) + 1);
+      if (!ctr->name)
+         return NVPM_ERROR_OUT_OF_MEMORY;
+
+      strncpy(ctr->name, info.name, strlen(info.name) + 1);
+      ctr->id = info.query_type;
+
+      list_addtail(&ctr->head, &ctx->counters);
+   }
+   return NVPM_OK;
+}
+
+/**
+ * Free memory allocated by the list of available counters.
+ */
+static void
+perfkit_free_counters(struct perfkit_context *ctx)
+{
+   struct perfkit_counter *ctr, *next;
+
+   LIST_FOR_EACH_ENTRY_SAFE(ctr, next, &ctx->counters, head) {
+      LIST_DEL(&ctr->head);
+      FREE(ctr->name);
+      FREE(ctr);
+   }
+}
+
+/**
+ * Get the context specified by ID.
+ */
+struct perfkit_context *
+perfkit_get_context(struct perfkit_device *dev, NVPMContext id)
+{
+   struct perfkit_context *ctx;
+
+   LIST_FOR_EACH_ENTRY(ctx, &dev->contexts, head) {
+      if (ctx->id == id)
+         return ctx;
+   }
+   return NULL;
+}
+
+/**
+ * Create a new context and store the list of available counters.
+ */
+static struct perfkit_context *
+perfkit_create_context(struct perfkit_device *dev)
+{
+   struct pipe_screen *pscreen;
+   struct perfkit_context *ctx;
+
+   ctx = CALLOC_STRUCT(perfkit_context);
+   if (!ctx)
+      return NULL;
+
+   pscreen = dev->vscreen->pscreen;
+   ctx->pipe = pscreen->context_create(pscreen, dev->vscreen);
+   if (!ctx->pipe)
+      goto no_pcontext;
+   ctx->id = ++dev->context_id; /* each context must have a different ID */
+
+   list_inithead(&ctx->counters);
+   list_inithead(&ctx->active_counters);
+
+   if (perfkit_query_counters(dev, ctx) != NVPM_OK)
+      goto no_memory;
+   return ctx;
+
+no_memory:
+   ctx->pipe->destroy(ctx->pipe);
+no_pcontext:
+   FREE(ctx);
+   return NULL;
+}
+
+/**
+ * Destroy the context specified by ID.
+ */
+static void
+perfkit_destroy_context(struct perfkit_context *ctx)
+{
+   perfkit_free_counters(ctx);
+   ctx->pipe->destroy(ctx->pipe);
+   LIST_DEL(&ctx->head);
+   FREE(ctx);
+}
+
+/**
+ * Create NVPMContext from OpenGL context.
+ */
+NVPMRESULT
+NVPMCreateContextFromOGLContext(APIContextHandle glCtx, NVPMContext *perfCtx)
+{
+   struct perfkit_context *ctx;
+
+   PERFKIT_GET_DEVICE(dev);
+
+   if (!perfCtx)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   ctx = perfkit_create_context(dev);
+   if (!ctx)
+       return NVPM_ERROR_OUT_OF_MEMORY;
+   *perfCtx = ctx->id;
+
+   list_addtail(&ctx->head, &dev->contexts);
+   return NVPM_OK;
+}
+
+/**
+ * Create NVPMContext from CUDA context.
+ */
+NVPMRESULT
+NVPMCreateContextFromCudaContext(APIContextHandle cuCtx, NVPMContext *perfCtx)
+{
+   /* No support for CUDA/OpenCL at the moment. */
+   return NVPM_NO_IMPLEMENTATION;
+}
+
+/**
+ * Destroy existing NVPMContext.
+ */
+NVPMRESULT NVPMDestroyContext(NVPMContext perfCtx)
+{
+   NVPMRESULT ret;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   /* Deactivate all counters for this context. */
+   ret = NVPMRemoveAllCounters(perfCtx);
+   if (ret != NVPM_OK)
+      return ret;
+
+   perfkit_destroy_context(ctx);
+   return NVPM_OK;
+}
diff --git a/src/gallium/state_trackers/perfkit/counter.c b/src/gallium/state_trackers/perfkit/counter.c
new file mode 100644
index 0000000..62fd63b
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/counter.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * 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 "pipe/p_context.h"
+#include "pipe/p_screen.h"
+
+#include "util/u_memory.h"
+
+#include "perfkit_private.h"
+
+#define NO_DESCRIPTION_AVAILABLE "(no description available)"
+
+static int
+perfkit_get_driver_query_info_by_type(struct pipe_screen *screen,
+                                      const unsigned type,
+                                      struct pipe_driver_query_info *pinfo)
+{
+    struct pipe_driver_query_info info;
+    unsigned num_queries, i;
+
+    if (!screen->get_driver_query_info)
+        return 0;
+
+    if (!pinfo)
+        return 0;
+
+    num_queries = screen->get_driver_query_info(screen, 0, NULL);
+    for (i = 0; i < num_queries; i++) {
+        if (screen->get_driver_query_info(screen, i, &info) &&
+            info.query_type == type) {
+            *pinfo = info;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static struct perfkit_counter *
+perfkit_get_counter_by_name(struct perfkit_context *ctx,
+                            const char *counterName)
+{
+   struct perfkit_counter *cntr;
+
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->counters, head) {
+      if (!strcmp(cntr->name, counterName))
+         return cntr;
+   }
+   return NULL;
+}
+
+static struct perfkit_counter *
+perfkit_get_counter_by_id(struct perfkit_context *ctx, NVPMCounterID counterID)
+{
+   struct perfkit_counter *cntr;
+
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->counters, head) {
+      if (cntr->id == counterID)
+         return cntr;
+   }
+   return NULL;
+}
+
+static int
+perfkit_counter_is_enabled(struct perfkit_context *ctx, const unsigned type)
+{
+   struct perfkit_query_counter *cntr;
+
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->active_counters, list) {
+      if (cntr->type == type)
+         return 1;
+   }
+   return 0;
+}
+
+static NVPMRESULT
+perfkit_enable_counter(struct perfkit_context *ctx, const unsigned type)
+{
+   struct perfkit_query_counter *cntr;
+
+   if (!perfkit_counter_is_enabled(ctx, type)) {
+      cntr = CALLOC_STRUCT(perfkit_query_counter);
+      if (!cntr)
+         return NVPM_ERROR_OUT_OF_MEMORY;
+      cntr->type = type;
+
+      LIST_ADDTAIL(&cntr->list, &ctx->active_counters);
+   }
+   return NVPM_OK;
+}
+
+static NVPMRESULT
+perfkit_disable_counter(struct perfkit_context *ctx, NVPMCounterID counterID)
+{
+   struct pipe_context *pipe = ctx->pipe;
+   struct perfkit_query_counter *cntr, *next;
+   unsigned i;
+
+   LIST_FOR_EACH_ENTRY_SAFE(cntr, next, &ctx->active_counters, list) {
+      if (cntr->type != counterID)
+         continue;
+
+      for (i = 0; i < ARRAY_SIZE(cntr->queries); i++) {
+         if (cntr->queries[i])
+            pipe->destroy_query(pipe, cntr->queries[i]);
+      }
+      LIST_DEL(&cntr->list);
+      FREE(cntr);
+      return NVPM_OK;
+   }
+   return NVPM_ERROR_COUNTER_NOT_ENABLED;
+}
+
+static NVPMRESULT
+perfkit_get_counter_value(struct perfkit_context *ctx, NVPMCounterID counterID,
+                          NVPMUINT64 *counterValue, NVPMUINT64 *counterCycles)
+{
+   struct perfkit_query_counter *cntr;
+   boolean found = FALSE;
+
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->active_counters, list) {
+      if (cntr->type == counterID) {
+         found = TRUE;
+         break;
+      }
+   }
+
+   if (!found)
+      return NVPM_ERROR_COUNTER_NOT_ENABLED;
+
+   *counterValue  = cntr->value;
+   *counterCycles = cntr->cycles;
+   return NVPM_OK;
+}
+
+/**
+ * Enumerate counters/experiments.
+ */
+NVPMRESULT
+NVPMEnumCountersByContext(NVPMContext perfCtx, NVPMEnumFunc enumFunction)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!enumFunction)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->counters, head) {
+      if (enumFunction(cntr->id, cntr->name) != NVPM_OK) {
+         /* Enumeration was stopped before the end of the counter
+          * list was reached because the callback function must
+          * return NVPM_OK to continue enumerating available
+          * counters. */
+         return NVPM_WARNING_ENDED_EARLY;
+      }
+   }
+   return NVPM_OK;
+}
+
+/**
+ * Get the name of a counter specified by ID.
+ */
+NVPMRESULT
+NVPMGetCounterName(NVPMCounterID counterID, char *counterName,
+                   NVPMUINT *plength)
+{
+   struct pipe_driver_query_info info;
+   struct pipe_screen *pscreen;
+   size_t length;
+
+   PERFKIT_GET_DEVICE(dev);
+
+   if (!counterName || !plength)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   pscreen = dev->vscreen->pscreen;
+   if (!perfkit_get_driver_query_info_by_type(pscreen, counterID, &info))
+      return NVPM_ERROR_INVALID_COUNTER;
+
+   /* Length must include null terminator. */
+   length = strlen(info.name) + 1;
+
+   if (*plength < length) {
+      *plength = length;
+      return NVPM_ERROR_STRING_TOO_SMALL;
+   }
+
+   strncpy(counterName, info.name, length);
+   return NVPM_OK;
+}
+
+/**
+ * Get the description of a counter specified by ID.
+ */
+NVPMRESULT
+NVPMGetCounterDescription(NVPMCounterID counterID, char *counterDesc,
+                          NVPMUINT *plength)
+{
+   struct pipe_driver_query_info info;
+   struct pipe_screen *pscreen;
+   size_t length;
+
+   PERFKIT_GET_DEVICE(dev);
+
+   if (!counterDesc || !plength)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   pscreen = dev->vscreen->pscreen;
+   if (!perfkit_get_driver_query_info_by_type(pscreen, counterID, &info))
+      return NVPM_ERROR_INVALID_COUNTER;
+
+   /* Length must include null terminator. */
+   length = strlen(NO_DESCRIPTION_AVAILABLE) + 1;
+
+   if (*plength < length) {
+      *plength = length;
+      return NVPM_ERROR_STRING_TOO_SMALL;
+   }
+
+   /* TODO: No description available at the moment. */
+   strncpy(counterDesc, NO_DESCRIPTION_AVAILABLE, length);
+   return NVPM_OK;
+}
+
+/**
+ * Get the ID of a counter specified by name for a given context.
+ */
+NVPMRESULT
+NVPMGetCounterIDByContext(NVPMContext perfCtx, char *counterName,
+                          NVPMCounterID *counterID)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterName || !counterID)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   cntr = perfkit_get_counter_by_name(ctx, counterName);
+   if (!cntr)
+      return NVPM_ERROR_INVALID_COUNTER;
+
+   *counterID = cntr->id;
+   return NVPM_OK;
+}
+
+/**
+ * Get the clock rate of a counter specified by name.
+ */
+NVPMRESULT
+NVPMGetCounterClockRateByContext(NVPMContext perfCtx, char *counterName,
+                                 float *counterClockValue)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterName || !counterClockValue)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   cntr = perfkit_get_counter_by_name(ctx, counterName);
+   if (!cntr)
+      return NVPM_ERROR_COUNTER_NOT_FOUND;
+
+   /* TODO: No counter clock rate value at the moment. */
+   *counterClockValue = 0;
+   return NVPM_OK;
+}
+
+/**
+ * Get the some attribute of a counter specified by ID.
+ */
+NVPMRESULT
+NVPMGetCounterAttribute(NVPMCounterID counterID, NVPMATTRIBUTE counterAttribute,
+                        NVPMUINT64 *counterValue)
+{
+   struct pipe_driver_query_info info;
+   struct pipe_screen *pscreen;
+
+   PERFKIT_GET_DEVICE(dev);
+
+   if (!counterValue)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   pscreen = dev->vscreen->pscreen;
+   if (!perfkit_get_driver_query_info_by_type(pscreen, counterID, &info))
+      return NVPM_ERROR_INVALID_COUNTER;
+
+   switch (counterAttribute) {
+      case NVPMA_COUNTER_TYPE:
+         /* TODO: Only GPU counters are currently exposed. */
+         *counterValue = NVPM_CT_GPU;
+         break;
+      case NVPMA_COUNTER_MAX:
+          *counterValue = info.max_value.u64;
+          break;
+      case NVPMA_COUNTER_DISPLAY:
+      case NVPMA_COUNTER_DOMAIN:
+          /* TODO: Not supported yet. */
+          return NVPM_NO_IMPLEMENTATION;
+      default:
+         unreachable("Invalid counter attribute");
+         break;
+   }
+
+   return NVPM_OK;
+}
+
+/**
+ * Activate counter specified by name.
+ */
+NVPMRESULT
+NVPMAddCounterByName(NVPMContext perfCtx, const char *counterName)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterName)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   cntr = perfkit_get_counter_by_name(ctx, counterName);
+   if (!cntr)
+      return NVPM_ERROR_COUNTER_NOT_FOUND;
+
+   return perfkit_enable_counter(ctx, cntr->id);
+}
+
+/**
+ * Activate counter specified by ID.
+ */
+NVPMRESULT
+NVPMAddCounter(NVPMContext perfCtx, NVPMCounterID counterID)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   cntr = perfkit_get_counter_by_id(ctx, counterID);
+   if (!cntr)
+      return NVPM_ERROR_COUNTER_NOT_FOUND;
+
+   return perfkit_enable_counter(ctx, cntr->id);
+}
+
+/**
+ * Activate multiple counters at a time specified by an ID array.
+ */
+NVPMRESULT
+NVPMAddCounters(NVPMContext perfCtx, NVPMUINT count, NVPMCounterID *counterIDs)
+{
+   NVPMRESULT ret;
+   NVPMUINT i;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterIDs)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   for (i = 0; i < count; i++) {
+      ret = NVPMAddCounter(perfCtx, counterIDs[i]);
+      if (ret != NVPM_OK)
+         return ret;
+   }
+   return NVPM_OK;
+}
+
+/**
+ * Deactivate counter specified by name.
+ */
+NVPMRESULT
+NVPMRemoveCounterByName(NVPMContext perfCtx, const char *counterName)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterName)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   cntr = perfkit_get_counter_by_name(ctx, counterName);
+   if (!cntr)
+      return NVPM_ERROR_COUNTER_NOT_FOUND;
+
+   return perfkit_disable_counter(ctx, cntr->id);
+}
+
+/**
+ * Deactivate counter specified by ID.
+ */
+NVPMRESULT
+NVPMRemoveCounter(NVPMContext perfCtx, NVPMCounterID counterID)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   cntr = perfkit_get_counter_by_id(ctx, counterID);
+   if (!cntr)
+      return NVPM_ERROR_COUNTER_NOT_FOUND;
+
+   return perfkit_disable_counter(ctx, cntr->id);
+}
+
+/**
+ * Deactivate multiple counters at a time specified by an ID array.
+ */
+NVPMRESULT
+NVPMRemoveCounters(NVPMContext perfCtx, NVPMUINT count,
+                   NVPMCounterID *counterIDs)
+{
+   NVPMRESULT ret;
+   NVPMUINT i;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterIDs)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   for (i = 0; i < count; i++) {
+      ret = NVPMRemoveCounter(perfCtx, counterIDs[i]);
+      if (ret != NVPM_OK)
+         return ret;
+   }
+   return NVPM_OK;
+}
+
+/**
+ * Deactivate all counters in the specified NVPMContext instance.
+ */
+NVPMRESULT
+NVPMRemoveAllCounters(NVPMContext perfCtx)
+{
+   struct perfkit_query_counter *cntr, *next;
+   NVPMRESULT ret;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   LIST_FOR_EACH_ENTRY_SAFE(cntr, next, &ctx->active_counters, list) {
+      ret = perfkit_disable_counter(ctx, cntr->type);
+      if (ret != NVPM_OK)
+         return ret;
+   }
+
+   return NVPM_OK;
+}
+
+/**
+ * Get value of a counter specified by name in a give NVPMPerfObject
+ * of a given NVPMPerfContext.
+ */
+NVPMRESULT
+NVPMGetCounterValueByName(NVPMContext perfCtx, const char *counterName,
+                          NVPMUINT objectID, NVPMUINT64 *counterValue,
+                          NVPMUINT64 *counterCycles)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterName || !counterValue || !counterCycles)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   cntr = perfkit_get_counter_by_name(ctx, counterName);
+   if (!cntr)
+      return NVPM_ERROR_COUNTER_NOT_FOUND;
+
+   return perfkit_get_counter_value(ctx, cntr->id, counterValue, counterCycles);
+}
+
+/**
+ * Get value of a counter specified by ID in a give NVPMPerfObject
+ * of a given NVPMPerfContext.
+ */
+NVPMRESULT
+NVPMGetCounterValue(NVPMContext perfCtx, NVPMCounterID counterID,
+                    NVPMUINT objectID, NVPMUINT64 *counterValue,
+                    NVPMUINT64 *counterCycles)
+{
+   struct perfkit_counter *cntr;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!counterValue || !counterCycles)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   cntr = perfkit_get_counter_by_id(ctx, counterID);
+   if (!cntr)
+      return NVPM_ERROR_COUNTER_NOT_FOUND;
+
+   return perfkit_get_counter_value(ctx, cntr->id, counterValue, counterCycles);
+}
+
+/**
+ * Convert bottleneck pipeline stage from ID to meaningful name string.
+ */
+NVPMRESULT
+NVPMGetGPUBottleneckName(NVPMContext perfCtx, NVPMUINT64 bottleneckID,
+                         char *counterName)
+{
+   /* TODO: No bottlenecks pipeline stage at the moment. */
+   return NVPM_NO_IMPLEMENTATION;
+}
diff --git a/src/gallium/state_trackers/perfkit/device.c b/src/gallium/state_trackers/perfkit/device.c
new file mode 100644
index 0000000..f97ccd0
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/device.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * 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 "pipe/p_state.h"
+
+#include "util/u_inlines.h"
+#include "util/u_memory.h"
+
+#include "perfkit_private.h"
+
+static struct perfkit_device *current_device = NULL;
+
+struct perfkit_device *
+perfkit_get_device(void)
+{
+   return current_device;
+}
+
+/**
+ * Init NVPMAPI.
+ */
+NVPMRESULT
+NVPMInit()
+{
+   struct perfkit_device *dev = NULL;
+   NVPMRESULT ret;
+
+   if (current_device)
+      return NVPM_ERROR_ALREADY_INITIALIZED;
+
+   current_device = dev = CALLOC_STRUCT(perfkit_device);
+   if (!dev)
+      return NVPM_ERROR_OUT_OF_MEMORY;
+
+   pipe_reference_init(&dev->reference, 1);
+
+   dev->display = XOpenDisplay(NULL);
+   if (!dev->display) {
+      ret = NVPM_FAILURE;
+      goto no_display;
+   }
+
+   dev->vscreen = vl_screen_create(dev->display, 0);
+   if (!dev->vscreen) {
+      ret = NVPM_ERROR_OUT_OF_MEMORY;
+      goto no_vscreen;
+   }
+
+   list_inithead(&dev->contexts);
+   return NVPM_OK;
+
+no_vscreen:
+   XCloseDisplay(dev->display);
+no_display:
+   FREE(dev);
+   return ret;
+}
+
+/**
+ * Shutdown NVPMAPI.
+ */
+NVPMRESULT
+NVPMShutdown()
+{
+   PERFKIT_GET_DEVICE(dev);
+
+   vl_screen_destroy(dev->vscreen);
+   XCloseDisplay(dev->display);
+   FREE(dev);
+   current_device = NULL;
+   return NVPM_OK;
+}
diff --git a/src/gallium/state_trackers/perfkit/entrypoint.c b/src/gallium/state_trackers/perfkit/entrypoint.c
new file mode 100644
index 0000000..4095378
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/entrypoint.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * 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 "util/u_memory.h"
+
+#include "perfkit_private.h"
+
+/**
+ * Get interface table.
+ */
+PUBLIC NVPMRESULT
+NVPMGetExportTable(const NVPM_UUID *tableID, void **ptable)
+{
+   NvPmApi *table;
+
+   if (!ptable)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   table = *ptable = CALLOC(1, sizeof(NvPmApi));
+   if (!table)
+      return NVPM_ERROR_OUT_OF_MEMORY;
+
+   table->SetWarningLevel = NVPMSetWarningLevel;
+   table->GetExtendedError = NVPMGetExtendedError;
+   table->Init = NVPMInit;
+   table->Shutdown = NVPMShutdown;
+   table->CreateContextFromOGLContext = NVPMCreateContextFromOGLContext;
+   table->CreateContextFromCudaContext = NVPMCreateContextFromCudaContext;
+   table->DestroyContext = NVPMDestroyContext;
+   table->EnumCountersByContext = NVPMEnumCountersByContext;
+   table->GetCounterName = NVPMGetCounterName;
+   table->GetCounterDescription = NVPMGetCounterDescription;
+   table->GetCounterIDByContext = NVPMGetCounterIDByContext;
+   table->GetCounterClockRateByContext = NVPMGetCounterClockRateByContext;
+   table->GetCounterAttribute = NVPMGetCounterAttribute;
+   table->AddCounterByName = NVPMAddCounterByName;
+   table->AddCounter = NVPMAddCounter;
+   table->AddCounters = NVPMAddCounters;
+   table->RemoveCounterByName = NVPMRemoveCounterByName;
+   table->RemoveCounter = NVPMRemoveCounter;
+   table->RemoveCounters = NVPMRemoveCounters;
+   table->RemoveAllCounters = NVPMRemoveAllCounters;
+   table->ReserveObjects = NVPMReserveObjects;
+   table->DeleteObjects = NVPMDeleteObjects;
+   table->BeginExperiment = NVPMBeginExperiment;
+   table->EndExperiment = NVPMEndExperiment;
+   table->BeginPass = NVPMBeginPass;
+   table->EndPass = NVPMEndPass;
+   table->BeginObject = NVPMBeginObject;
+   table->EndObject = NVPMEndObject;
+   table->Sample = NVPMSample;
+   table->SampleEx = NVPMSampleEx;
+   table->GetCounterValueByName = NVPMGetCounterValueByName;
+   table->GetCounterValue = NVPMGetCounterValue;
+   table->GetGPUBottleneckName = NVPMGetGPUBottleneckName;
+   table->RegisterNewDataProviderCallback = NVPMRegisterNewDataProviderCallback;
+
+   return NVPM_OK;
+}
diff --git a/src/gallium/state_trackers/perfkit/monitoring.c b/src/gallium/state_trackers/perfkit/monitoring.c
new file mode 100644
index 0000000..34ac1a0
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/monitoring.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * 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 <stdio.h>
+
+#include "util/u_debug.h"
+
+#include "pipe/p_context.h"
+
+#include "perfkit_private.h"
+
+static NVPMRESULT
+perfkit_sample_counter(struct perfkit_context *ctx,
+                       struct perfkit_query_counter *cntr)
+{
+   struct pipe_context *pipe = ctx->pipe;
+
+   if (cntr->num_queries) {
+      pipe->end_query(pipe, cntr->queries[cntr->head]);
+
+      /* read query results */
+      while (1) {
+         struct pipe_query *query = cntr->queries[cntr->tail];
+         union pipe_query_result result;
+         uint64_t *res64 = (uint64_t *)&result;
+
+         if (pipe->get_query_result(pipe, query, FALSE, &result)) {
+            cntr->results_cumulative += res64[0];
+            cntr->num_results++;
+
+            if (cntr->tail == cntr->head)
+               break;
+
+            cntr->tail = (cntr->tail + 1) % PERFKIT_NUM_QUERIES;
+         } else {
+            /* the oldest query is busy */
+            if ((cntr->head + 1) % PERFKIT_NUM_QUERIES == cntr->tail) {
+               /* all queries are busy, throw away the last query and create
+                * a new one */
+               debug_printf("perfkit: all queries are busy after %i frames, "
+                            "can't add another query\n", PERFKIT_NUM_QUERIES);
+               pipe->destroy_query(pipe, cntr->queries[cntr->head]);
+               cntr->queries[cntr->head] =
+                  pipe->create_query(pipe, cntr->type, 0);
+               if (!cntr->queries[cntr->head])
+                  return NVPM_ERROR_OUT_OF_MEMORY;
+            } else {
+               /* the last query is busy, we need to add a new one we can use
+                * for this frame */
+               cntr->head = (cntr->head + 1) % PERFKIT_NUM_QUERIES;
+               if (!cntr->queries[cntr->head]) {
+                  cntr->queries[cntr->head] =
+                     pipe->create_query(pipe, cntr->type, 0);
+                  if (!cntr->queries[cntr->head])
+                     return NVPM_ERROR_OUT_OF_MEMORY;
+                  cntr->num_queries++;
+               }
+            }
+            break;
+         }
+      }
+
+      if (cntr->num_results) {
+         /* compute the average values across all queries */
+         cntr->value = cntr->results_cumulative / cntr->num_results;
+         cntr->cycles = 1; /* TODO: No counter cycles exposed for now. */
+
+         cntr->results_cumulative = 0;
+         cntr->num_results = 0;
+      }
+   } else {
+      cntr->queries[cntr->head] = pipe->create_query(pipe, cntr->type, 0);
+      if (!cntr->queries[cntr->head])
+         return NVPM_ERROR_OUT_OF_MEMORY;
+      cntr->num_queries++;
+   }
+
+   if (!pipe->begin_query(pipe, cntr->queries[cntr->head]))
+      return NVPM_ERROR_INTERNAL;
+
+   return NVPM_OK;
+}
+
+/**
+ * Sample active counters for a specified NVPMContext and output active
+ * counter information.
+ */
+NVPMRESULT
+NVPMSample(NVPMContext perfCtx, NVPMSampleValue *samples, NVPMUINT *count)
+{
+   struct perfkit_query_counter *cntr;
+   NVPMRESULT ret;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (ctx->experiment.active) {
+      /* Cannot sample active counters while an experiment is running. */
+      return NVPM_ERROR_STATE_MACHINE;
+   }
+
+   if (!samples && !count) {
+      /* When samples and count are both NULL, will not update the
+       * active counter data. */
+      return NVPM_OK;
+   }
+
+   if (count)
+      *count = 0;
+
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->active_counters, list) {
+      ret = perfkit_sample_counter(ctx, cntr);
+      if (ret != NVPM_OK)
+         return ret;
+
+      if (!count) {
+         /* When count is NULL, no counter data will be output to samples,
+          * and count will set to 0. */
+         continue;
+      }
+
+      if (samples) {
+          NVPMSampleValue *sample = &samples[*count];
+
+          /* Update counter information. */
+          sample->unCounterID = cntr->type;
+          sample->ulValue     = cntr->value;
+          sample->ulCycles    = cntr->cycles;
+      }
+
+      /* Update the number of counter information saved to samples. */
+      (*count)++;
+   }
+
+   return NVPM_OK;
+}
+
+/**
+ * Extended version of NVPMSample.
+ */
+NVPMRESULT
+NVPMSampleEx(NVPMContext perfCtx, NVPMSampleValueEx *samples,
+             NVPMUINT *count)
+{
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+   return NVPM_NO_IMPLEMENTATION;
+}
+
+/**
+ * Begin experiment.
+ */
+NVPMRESULT
+NVPMBeginExperiment(NVPMContext perfCtx, NVPMUINT *numPasses)
+{
+   struct perfkit_query_counter *cntr;
+   struct pipe_context *pipe;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!numPasses)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   if (ctx->experiment.active) {
+      /* Multiple experiments are not allowed. */
+      return NVPM_ERROR_STATE_MACHINE;
+   }
+
+   /* Init the default number of passes. */
+   *numPasses = 0;
+
+   /* Create a new query for each active counters. */
+   pipe = ctx->pipe;
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->active_counters, list) {
+      cntr->queries[cntr->head] = pipe->create_query(pipe, cntr->type, 0);
+      if (!cntr->queries[cntr->head])
+         return NVPM_ERROR_OUT_OF_MEMORY;
+      (*numPasses)++;
+   }
+
+   ctx->experiment.num_passes = *numPasses;
+   ctx->experiment.cur_pass   = -1;
+   ctx->experiment.active     = true;
+   return NVPM_OK;
+}
+
+/**
+ * Ending an experiment.
+ */
+NVPMRESULT
+NVPMEndExperiment(NVPMContext perfCtx)
+{
+   struct perfkit_query_counter *cntr;
+   struct pipe_context *pipe;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!ctx->experiment.active) {
+      /* No experiment is currently running. */
+      return NVPM_ERROR_STATE_MACHINE;
+   }
+
+   if (ctx->experiment.cur_pass < ctx->experiment.num_passes - 1) {
+      /* Incomplete number of passes processed. */
+      return NVPM_ERROR_EXPERIMENT_INCOMPLETE;
+   }
+
+   /* Read query results for each active counters. */
+   pipe = ctx->pipe;
+   LIST_FOR_EACH_ENTRY(cntr, &ctx->active_counters, list) {
+      union pipe_query_result result;
+      uint64_t *res64 = (uint64_t *)&result;
+
+      if (!pipe->get_query_result(pipe, cntr->queries[cntr->head],
+                                  TRUE, &result))
+         continue;
+      cntr->value  = res64[0];
+      cntr->cycles = 1;
+
+      pipe->destroy_query(pipe, cntr->queries[cntr->head]);
+      cntr->queries[cntr->head] = NULL;
+   }
+
+   ctx->experiment.num_passes = 0;
+   ctx->experiment.cur_pass   = -1;
+   ctx->experiment.active     = false;
+   return NVPM_OK;
+}
+
+/**
+ * Beginning a pass.
+ */
+NVPMRESULT
+NVPMBeginPass(NVPMContext perfCtx, NVPMUINT pass)
+{
+   struct perfkit_query_counter *cntr;
+   struct pipe_context *pipe;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!ctx->experiment.active)
+      return NVPM_ERROR_STATE_MACHINE;
+
+   if (!ctx->experiment.num_passes || ctx->experiment.cur_pass + 1 != pass)
+      return NVPM_ERROR_INVALID_PASS;
+   ctx->experiment.cur_pass++;
+
+   /* Get the first active counter. */
+   cntr = LIST_FIRST_ENTRY(struct perfkit_query_counter,
+                           &ctx->active_counters, list);
+
+   /* Start the query for this counter. */
+   pipe = ctx->pipe;
+   if (!pipe->begin_query(pipe, cntr->queries[cntr->head]))
+      return NVPM_ERROR_INTERNAL;
+
+   return NVPM_OK;
+}
+
+/**
+ * Ending a pass.
+ */
+NVPMRESULT
+NVPMEndPass(NVPMContext perfCtx, NVPMUINT pass)
+{
+   struct perfkit_query_counter *cntr;
+   struct pipe_context *pipe;
+
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!ctx->experiment.active)
+      return NVPM_ERROR_STATE_MACHINE;
+
+   if (ctx->experiment.cur_pass != pass)
+      return NVPM_ERROR_INVALID_PASS;
+
+   /* Get the first active counter. */
+   cntr = LIST_FIRST_ENTRY(struct perfkit_query_counter,
+                           &ctx->active_counters, list);
+
+   /* Stop the query for this counter. */
+   pipe = ctx->pipe;
+   pipe->end_query(pipe, cntr->queries[cntr->head]);
+
+   /* Round robbin. */
+   LIST_MOVE_TAIL(&cntr->list, &ctx->active_counters);
+
+   return NVPM_OK;
+}
+
+/**
+ * Reserve certain amount of NVPMPerfObjects.
+ */
+NVPMRESULT
+NVPMReserveObjects(NVPMContext perfCtx, NVPMUINT numObjects)
+{
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   /* TODO: Figure out what this functions does... */
+   return NVPM_NO_IMPLEMENTATION;
+}
+
+/**
+ * Delete all NVPMPerfObjects in a given NVPMPerfContext.
+ */
+NVPMRESULT
+NVPMDeleteObjects(NVPMContext perfCtx)
+{
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   /* TODO: Figure out what this functions does... */
+   return NVPM_NO_IMPLEMENTATION;
+}
+
+/**
+ * Beginning a NVPMPerfObject, make that NVPMPerfObject active.
+ */
+NVPMRESULT
+NVPMBeginObject(NVPMContext perfCtx, NVPMUINT objectID)
+{
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!ctx->experiment.active)
+      return NVPM_ERROR_STATE_MACHINE;
+
+   /* TODO: Figure out what this functions does... */
+   return NVPM_NO_IMPLEMENTATION;
+}
+
+/**
+ * Ending of a NVPMPerfObject, make that NVPMPerfObject inactive.
+ */
+NVPMRESULT
+NVPMEndObject(NVPMContext perfCtx, NVPMUINT objectID)
+{
+   PERFKIT_GET_CONTEXT(ctx, perfCtx);
+
+   if (!ctx->experiment.active)
+      return NVPM_ERROR_STATE_MACHINE;
+
+   /* TODO: Figure out what this functions does... */
+   return NVPM_NO_IMPLEMENTATION;
+}
diff --git a/src/gallium/state_trackers/perfkit/perfkit_private.h b/src/gallium/state_trackers/perfkit/perfkit_private.h
new file mode 100644
index 0000000..a128152
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/perfkit_private.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * 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.
+ */
+
+#ifndef PERFKIT_PRIVATE_H
+#define PERFKIT_PRIVATE_H
+
+#include <perfkit/NvPmApi.h>
+
+#include "pipe/p_state.h"
+
+#include "util/list.h"
+
+#include "vl/vl_winsys.h"
+
+#define PERFKIT_NUM_QUERIES 8
+
+#define PERFKIT_GET_DEVICE(D)                                                 \
+   struct perfkit_device *D = perfkit_get_device();                           \
+   if (!D)                                                                    \
+      return NVPM_ERROR_NOT_INITIALIZED;
+
+#define PERFKIT_GET_CONTEXT(C, ID)                                            \
+   PERFKIT_GET_DEVICE(D)                                                      \
+   struct perfkit_context *C = perfkit_get_context(D, ID);                    \
+   if (!C)                                                                    \
+      return NVPM_ERROR_INVALID_PARAMETER;                                    \
+
+/* Public function of NVPMAPI */
+NVPMRESULT NVPMGetExportTable(const NVPM_UUID *, void **);
+
+/* Internal function pointers of NVPMAPI */
+NVPMRESULT NVPMSetWarningLevel(NVPMUINT);
+NVPMRESULT NVPMGetExtendedError(NVPMUINT *);
+NVPMRESULT NVPMInit(void);
+NVPMRESULT NVPMShutdown(void);
+NVPMRESULT NVPMCreateContextFromOGLContext(APIContextHandle, NVPMContext *);
+NVPMRESULT NVPMCreateContextFromCudaContext(APIContextHandle, NVPMContext *);
+NVPMRESULT NVPMDestroyContext(NVPMContext);
+NVPMRESULT NVPMEnumCountersByContext(NVPMContext, NVPMEnumFunc);
+NVPMRESULT NVPMGetCounterName(NVPMCounterID, char *, NVPMUINT *);
+NVPMRESULT NVPMGetCounterDescription(NVPMCounterID, char *, NVPMUINT *);
+NVPMRESULT NVPMGetCounterIDByContext(NVPMContext, char *, NVPMCounterID *);
+NVPMRESULT NVPMGetCounterClockRateByContext(NVPMContext, char *, float *);
+NVPMRESULT NVPMGetCounterAttribute(NVPMCounterID, NVPMATTRIBUTE, NVPMUINT64 *);
+NVPMRESULT NVPMAddCounterByName(NVPMContext, const char *);
+NVPMRESULT NVPMAddCounter(NVPMContext, NVPMCounterID);
+NVPMRESULT NVPMAddCounters(NVPMContext, NVPMUINT, NVPMCounterID *);
+NVPMRESULT NVPMRemoveCounterByName(NVPMContext, const char *);
+NVPMRESULT NVPMRemoveCounter(NVPMContext, NVPMCounterID);
+NVPMRESULT NVPMRemoveCounters(NVPMContext, NVPMUINT, NVPMCounterID *);
+NVPMRESULT NVPMRemoveAllCounters(NVPMContext);
+NVPMRESULT NVPMReserveObjects(NVPMContext, NVPMUINT);
+NVPMRESULT NVPMDeleteObjects(NVPMContext);
+NVPMRESULT NVPMBeginExperiment(NVPMContext, NVPMUINT *);
+NVPMRESULT NVPMEndExperiment(NVPMContext);
+NVPMRESULT NVPMBeginPass(NVPMContext, NVPMUINT);
+NVPMRESULT NVPMEndPass(NVPMContext, NVPMUINT);
+NVPMRESULT NVPMBeginObject(NVPMContext, NVPMUINT);
+NVPMRESULT NVPMEndObject(NVPMContext, NVPMUINT);
+NVPMRESULT NVPMSample(NVPMContext, NVPMSampleValue *, NVPMUINT *);
+NVPMRESULT NVPMSampleEx(NVPMContext, NVPMSampleValueEx *, NVPMUINT *);
+NVPMRESULT NVPMGetCounterValueByName(NVPMContext, const char *, NVPMUINT,
+                                     NVPMUINT64 *, NVPMUINT64 *);
+NVPMRESULT NVPMGetCounterValue(NVPMContext, NVPMCounterID, NVPMUINT,
+                               NVPMUINT64 *, NVPMUINT64 *);
+NVPMRESULT NVPMGetGPUBottleneckName(NVPMContext, NVPMUINT64, char *);
+NVPMRESULT NVPMRegisterNewDataProviderCallback(FuncPtrNewDataProvider);
+
+/* Internal data structs */
+struct perfkit_query_counter
+{
+   struct list_head list;
+
+   NVPMCounterID type;
+   NVPMUINT64 value, cycles;
+
+   uint64_t results_cumulative;
+   unsigned num_results;
+
+   /* Ring of queries. If a query is busy, we use another slot. */
+   struct pipe_query *queries[PERFKIT_NUM_QUERIES];
+   unsigned head, tail;
+   unsigned num_queries;
+};
+
+struct perfkit_counter
+{
+   struct list_head head;
+   NVPMCounterID id;
+   char *name;
+};
+
+struct perfkit_context
+{
+   struct pipe_context *pipe;
+   struct list_head head;
+   NVPMContext id;
+
+   /* list of available counters */
+   struct list_head counters;
+
+   /* list of enabled counters */
+   struct list_head active_counters;
+
+   struct {
+      bool active;
+      unsigned num_passes;
+      int cur_pass;
+   } experiment;
+};
+
+struct perfkit_device
+{
+   struct pipe_reference reference;
+   struct list_head contexts;
+   NVPMContext context_id;
+   struct vl_screen *vscreen;
+   Display *display;
+};
+
+/* Internal functions */
+struct perfkit_device *perfkit_get_device(void);
+struct perfkit_context *perfkit_get_context(struct perfkit_device *dev,
+                                            NVPMContext perfCtx);
+
+#endif /* PERFKIT_PRIVATE_H */
diff --git a/src/gallium/state_trackers/perfkit/util.c b/src/gallium/state_trackers/perfkit/util.c
new file mode 100644
index 0000000..5c570dc
--- /dev/null
+++ b/src/gallium/state_trackers/perfkit/util.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 Samuel Pitoiset
+ *
+ * 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 "perfkit_private.h"
+
+/**
+ * Set warning output level to be set.
+ */
+NVPMRESULT
+NVPMSetWarningLevel(NVPMUINT level)
+{
+   /* NVIDIA Perfkit 3.0.1 doesn't seem to implement this function. */
+   return NVPM_NO_IMPLEMENTATION;
+}
+
+/**
+ * Get extended error code.
+ */
+NVPMRESULT
+NVPMGetExtendedError(NVPMUINT *error)
+{
+   /* NVIDIA Perfkit 3.0.1 doesn't seem to implement this function. */
+   return NVPM_NO_IMPLEMENTATION;
+}
+
+/**
+ * Register a callback function to be called when new data provider is
+ * registered to the NVPMAPI module.
+ */
+NVPMRESULT
+NVPMRegisterNewDataProviderCallback(FuncPtrNewDataProvider callback)
+{
+   if (!callback)
+      return NVPM_ERROR_INVALID_PARAMETER;
+
+   return NVPM_NO_IMPLEMENTATION;
+}
diff --git a/src/gallium/targets/dri-perfkit.dyn b/src/gallium/targets/dri-perfkit.dyn
new file mode 100644
index 0000000..a10356b
--- /dev/null
+++ b/src/gallium/targets/dri-perfkit.dyn
@@ -0,0 +1,3 @@
+{
+	nouveau_drm_screen_create;
+};
diff --git a/src/gallium/targets/perfkit/Makefile.am b/src/gallium/targets/perfkit/Makefile.am
new file mode 100644
index 0000000..9e3a054
--- /dev/null
+++ b/src/gallium/targets/perfkit/Makefile.am
@@ -0,0 +1,123 @@
+include $(top_srcdir)/src/gallium/Automake.inc
+
+AM_CFLAGS = \
+	$(GALLIUM_TARGET_CFLAGS)
+
+perfkitdir = $(PERFKIT_LIB_INSTALL_DIR)
+perfkit_LTLIBRARIES = libperfkit_gallium.la
+
+nodist_EXTRA_libperfkit_gallium_la_SOURCES = dummy.cpp
+libperfkit_gallium_la_SOURCES =
+
+libperfkit_gallium_la_LDFLAGS = \
+	-shared \
+	-module \
+	-no-undefined \
+	-version-number $(PERFKIT_MAJOR):$(PERFKIT_MINOR) \
+	$(GC_SECTIONS) \
+	$(LD_NO_UNDEFINED)
+
+if HAVE_LD_VERSION_SCRIPT
+libperfkit_gallium_la_LDFLAGS += \
+	-Wl,--version-script=$(top_srcdir)/src/gallium/targets/perfkit/perfkit.sym
+endif # HAVE_LD_VERSION_SCRIPT
+
+if HAVE_LD_DYNAMIC_LIST
+libperfkit_gallium_la_LDFLAGS += \
+	-Wl,--dynamic-list=$(top_srcdir)/src/gallium/targets/dri-perfkit.dyn
+endif # HAVE_LD_DYNAMIC_LIST
+
+# NOTE: libperfkit_gallium does not use(link against) libperfkit
+libperfkit_gallium_la_LIBADD = \
+	$(top_builddir)/src/gallium/state_trackers/perfkit/libperfkittracker.la \
+	$(top_builddir)/src/gallium/auxiliary/libgalliumvlwinsys.la \
+	$(top_builddir)/src/gallium/auxiliary/libgalliumvl.la \
+	$(top_builddir)/src/gallium/auxiliary/libgallium.la \
+	$(top_builddir)/src/util/libmesautil.la \
+	$(VL_LIBS) \
+	$(LIBDRM_LIBS) \
+	$(GALLIUM_COMMON_LIB_DEPS)
+
+EXTRA_libperfkit_gallium_la_DEPENDENCIES = \
+	perfkit.sym \
+	$(top_srcdir)/src/gallium/targets/dri-perfkit.dyn
+EXTRA_DIST = \
+	perfkit.sym \
+	$(top_srcdir)/src/gallium/targets/dri-perfkit.dyn
+
+TARGET_DRIVERS =
+TARGET_CPPFLAGS =
+TARGET_LIB_DEPS = $(top_builddir)/src/loader/libloader.la
+
+include $(top_srcdir)/src/gallium/drivers/nouveau/Automake.inc
+
+if HAVE_GALLIUM_STATIC_TARGETS
+
+libperfkit_gallium_la_SOURCES += target.c
+libperfkit_gallium_la_CPPFLAGS = $(TARGET_CPPFLAGS)
+libperfkit_gallium_la_LIBADD += $(TARGET_LIB_DEPS) \
+	$(TARGET_RADEON_WINSYS) $(TARGET_RADEON_COMMON)
+
+else # HAVE_GALLIUM_STATIC_TARGETS
+
+libperfkit_gallium_la_LIBADD += \
+	$(top_builddir)/src/gallium/auxiliary/pipe-loader/libpipe_loader.la \
+	$(GALLIUM_PIPE_LOADER_WINSYS_LIBS) \
+	$(GALLIUM_PIPE_LOADER_LIBS)
+
+endif # HAVE_GALLIUM_STATIC_TARGETS
+
+if HAVE_MESA_LLVM
+libperfkit_gallium_la_LIBADD += $(LLVM_LIBS)
+libperfkit_gallium_la_LDFLAGS += $(LLVM_LDFLAGS)
+endif
+
+if HAVE_COMPAT_SYMLINKS
+# Add a link to allow setting PERFKIT_DRIVER_PATH to /lib/gallium of the build tree.
+all-local: $(perfkit_LTLIBRARIES)
+	$(AM_V_GEN)link_dir=$(top_builddir)/$(LIB_DIR)/gallium;		\
+	$(MKDIR_P) $${link_dir};					\
+	for i in $(TARGET_DRIVERS); do					\
+		j=libperfkit_gallium.$(LIB_EXT);				\
+		k=libperfkit_$${i}.$(LIB_EXT);				\
+		l=$${k}.$(PERFKIT_MAJOR).$(PERFKIT_MINOR).0;		\
+		ln -f .libs/$${j}.$(PERFKIT_MAJOR).$(PERFKIT_MINOR).0	\
+		      $${link_dir}/$${l};				\
+		ln -sf $${l}						\
+		       $${link_dir}/$${k}.$(PERFKIT_MAJOR).$(PERFKIT_MINOR); \
+		ln -sf $${l}						\
+		       $${link_dir}/$${k}.$(PERFKIT_MAJOR);		\
+		ln -sf $${l}						\
+		       $${link_dir}/$${k};				\
+	done
+
+clean-local:
+	$(AM_V_GEN)link_dir=$(top_builddir)/$(LIB_DIR)/gallium;		\
+	$(AM_V_GEN)for i in $(TARGET_DRIVERS); do			\
+		$(RM) $${link_dir}/libperfkit_$${i}.so{,.$(PERFKIT_MAJOR){,.$(PERFKIT_MINOR){,.0}}}; \
+	done;
+endif
+
+# hardlink each megadriver instance, but don't actually have
+# libperfkit_gallium.so in the set of final installed files.
+install-data-hook:
+	$(AM_V_GEN)dest_dir=$(DESTDIR)/$(perfkitdir);			\
+	for i in $(TARGET_DRIVERS); do					\
+		j=libperfkit_gallium.$(LIB_EXT);				\
+		k=libperfkit_$${i}.$(LIB_EXT);				\
+		l=$${k}.$(PERFKIT_MAJOR).$(PERFKIT_MINOR).0;		\
+		ln -f $${dest_dir}/$${j}.$(PERFKIT_MAJOR).$(PERFKIT_MINOR).0 \
+		      $${dest_dir}/$${l};				\
+		ln -sf $${l}						\
+		       $${dest_dir}/$${k}.$(PERFKIT_MAJOR).$(PERFKIT_MINOR); \
+		ln -sf $${l}						\
+		       $${dest_dir}/$${k}.$(PERFKIT_MAJOR);		\
+		ln -sf $${l}						\
+		       $${dest_dir}/$${k};				\
+	done;								\
+	$(RM) $${dest_dir}/libperfkit_gallium.*
+
+uninstall-hook:
+	for i in $(TARGET_DRIVERS); do					\
+		$(RM) $(DESTDIR)$(perfkitdir)/libperfkit_$${i}.so{,.$(PERFKIT_MAJOR){,.$(PERFKIT_MINOR){,.0}}}; \
+	done;
diff --git a/src/gallium/targets/perfkit/perfkit.sym b/src/gallium/targets/perfkit/perfkit.sym
new file mode 100644
index 0000000..66d6c9c
--- /dev/null
+++ b/src/gallium/targets/perfkit/perfkit.sym
@@ -0,0 +1,7 @@
+{
+       global:
+               NVPMGetExportTable;
+               nouveau_drm_screen_create;
+       local:
+               *;
+};
diff --git a/src/gallium/targets/perfkit/target.c b/src/gallium/targets/perfkit/target.c
new file mode 100644
index 0000000..fde4a4a
--- /dev/null
+++ b/src/gallium/targets/perfkit/target.c
@@ -0,0 +1 @@
+#include "target-helpers/inline_drm_helper.h"
-- 
2.4.2



More information about the Nouveau mailing list