iris: implement iris layer of INTEL_MEASURE
authorMark Janes <markjanes@swizzler.org>
Mon, 11 May 2020 20:51:45 +0000 (13:51 -0700)
committerMark Janes <markjanes@swizzler.org>
Tue, 2 Feb 2021 01:24:57 +0000 (17:24 -0800)
Acked-by: Kenneth Graunke <kenneth@whitecape.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7354>

src/gallium/drivers/iris/iris_batch.h
src/gallium/drivers/iris/iris_measure.c [new file with mode: 0644]
src/gallium/drivers/iris/iris_measure.h [new file with mode: 0644]
src/gallium/drivers/iris/iris_screen.h
src/gallium/drivers/iris/meson.build

index 2f05303..76927eb 100644 (file)
@@ -168,6 +168,7 @@ struct iris_batch {
    uint32_t sync_region_depth;
 
    uint32_t last_aux_map_state;
+   struct iris_measure_batch *measure;
 };
 
 void iris_init_batch(struct iris_context *ice,
diff --git a/src/gallium/drivers/iris/iris_measure.c b/src/gallium/drivers/iris/iris_measure.c
new file mode 100644 (file)
index 0000000..9013af0
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * 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.
+ */
+
+/**
+ * @file iris_measure.c
+ */
+
+#include <stdio.h>
+#include "util/debug.h"
+#include "util/list.h"
+#include "util/crc32.h"
+#include "iris_context.h"
+#include "iris_defines.h"
+
+void
+iris_init_screen_measure(struct iris_screen *screen)
+{
+   struct intel_measure_device *measure_device = &screen->measure;
+
+   memset(measure_device, 0, sizeof(*measure_device));
+   intel_measure_init(measure_device);
+   struct intel_measure_config *config = measure_device->config;
+   if (config == NULL)
+      return;
+
+   list_inithead(&measure_device->queued_snapshots);
+   pthread_mutex_init(&measure_device->mutex, NULL);
+
+   /* the final member of intel_measure_ringbuffer is a zero-length array of
+    * intel_measure_buffered_result objects.  Allocate additional space for
+    * the buffered objects based on the run-time configurable buffer_size
+    */
+   const size_t rb_bytes = sizeof(struct intel_measure_ringbuffer) +
+      config->buffer_size * sizeof(struct intel_measure_buffered_result);
+   struct intel_measure_ringbuffer *rb = rzalloc_size(screen, rb_bytes);
+   measure_device->ringbuffer = rb;
+}
+
+static struct intel_measure_config *
+config_from_screen(struct iris_screen *screen)
+{
+   return screen->measure.config;
+}
+
+static struct intel_measure_config *
+config_from_context(struct iris_context *ice)
+{
+   return ((struct iris_screen *) ice->ctx.screen)->measure.config;
+}
+
+void
+iris_destroy_screen_measure(struct iris_screen *screen)
+{
+   if (!config_from_screen(screen))
+      return;
+
+   struct intel_measure_device *measure_device = &screen->measure;
+
+   if (measure_device->config->file &&
+       measure_device->config->file != stderr)
+      fclose(screen->measure.config->file);
+
+   ralloc_free(measure_device->ringbuffer);
+   measure_device->ringbuffer = NULL;
+}
+
+
+void
+iris_init_batch_measure(struct iris_context *ice, struct iris_batch *batch)
+{
+   const struct intel_measure_config *config = config_from_context(ice);
+   struct iris_screen *screen = batch->screen;
+   struct iris_bufmgr *bufmgr = screen->bufmgr;
+
+   if (!config)
+      return;
+
+   /* the final member of iris_measure_batch is a zero-length array of
+    * intel_measure_snapshot objects.  Create additional space for the
+    * snapshot objects based on the run-time configurable batch_size
+    */
+   const size_t batch_bytes = sizeof(struct iris_measure_batch) +
+      config->batch_size * sizeof(struct intel_measure_snapshot);
+   assert(batch->measure == NULL);
+   batch->measure = malloc(batch_bytes);
+   memset(batch->measure, 0, batch_bytes);
+   struct iris_measure_batch *measure = batch->measure;
+
+   measure->bo =
+      iris_bo_alloc_tiled(bufmgr, "measure",
+                          config->batch_size * sizeof(uint64_t),
+                          1,  /* alignment */
+                          IRIS_MEMZONE_OTHER,
+                          I915_TILING_NONE,
+                          0, /* pitch */
+                          BO_ALLOC_ZEROED);
+
+   measure->base.framebuffer =
+      (uintptr_t)util_hash_crc32(&ice->state.framebuffer,
+                                 sizeof(ice->state.framebuffer));
+}
+
+static bool
+iris_measure_ready(struct iris_measure_batch *measure)
+{
+   return !iris_bo_busy(measure->bo);
+}
+
+void
+iris_destroy_batch_measure(struct iris_measure_batch *batch)
+{
+   if (!batch)
+      return;
+
+   iris_bo_unreference(batch->bo);
+   batch->bo = NULL;
+   free(batch);
+}
+
+static void
+measure_start_snapshot(struct iris_context *ice,
+                       struct iris_batch *batch,
+                       enum intel_measure_snapshot_type type,
+                       const char *event_name,
+                       uint32_t count)
+{
+   struct intel_measure_batch *measure_batch = &batch->measure->base;
+   const struct intel_measure_config *config = config_from_context(ice);
+   const struct iris_screen *screen = (void *) ice->ctx.screen;
+   const unsigned screen_frame = screen->measure.frame;
+
+   /* if the command buffer is not associated with a frame, associate it with
+    * the most recent acquired frame
+    */
+   if (measure_batch->frame == 0)
+      measure_batch->frame = screen_frame;
+
+   uintptr_t framebuffer = measure_batch->framebuffer;
+
+   if (measure_batch->index == config->batch_size) {
+      /* Snapshot buffer is full.  The batch must be flushed before additional
+       * snapshots can be taken.
+       */
+      static bool warned = false;
+      if (unlikely(!warned)) {
+         fprintf(config->file,
+                 "WARNING: batch size exceeds INTEL_MEASURE limit: %d. "
+                 "Data has been dropped. "
+                 "Increase setting with INTEL_MEASURE=batch_size={count}\n",
+                 config->batch_size);
+         warned = true;
+      }
+      return;
+   }
+
+   unsigned index = measure_batch->index++;
+   assert(index < config->batch_size);
+   iris_emit_pipe_control_write(batch, "measurement snapshot",
+                                PIPE_CONTROL_WRITE_TIMESTAMP |
+                                PIPE_CONTROL_CS_STALL,
+                                batch->measure->bo, index * sizeof(uint64_t), 0ull);
+   if (event_name == NULL)
+      event_name = intel_measure_snapshot_string(type);
+
+   struct intel_measure_snapshot *snapshot = &(measure_batch->snapshots[index]);
+   memset(snapshot, 0, sizeof(*snapshot));
+   snapshot->type = type;
+   snapshot->count = (unsigned) count;
+   snapshot->event_count = measure_batch->event_count;
+   snapshot->event_name = event_name;
+   snapshot->framebuffer = framebuffer;
+
+   if (type == INTEL_SNAPSHOT_COMPUTE) {
+      snapshot->cs = (uintptr_t) ice->shaders.prog[MESA_SHADER_COMPUTE];
+   } else {
+      snapshot->vs  = (uintptr_t) ice->shaders.prog[MESA_SHADER_VERTEX];
+      snapshot->tcs = (uintptr_t) ice->shaders.prog[MESA_SHADER_TESS_CTRL];
+      snapshot->tes = (uintptr_t) ice->shaders.prog[MESA_SHADER_TESS_EVAL];
+      snapshot->gs  = (uintptr_t) ice->shaders.prog[MESA_SHADER_GEOMETRY];
+      snapshot->fs  = (uintptr_t) ice->shaders.prog[MESA_SHADER_FRAGMENT];
+   }
+}
+
+static void
+measure_end_snapshot(struct iris_batch *batch,
+                     uint32_t event_count)
+{
+   struct intel_measure_batch *measure_batch = &batch->measure->base;
+
+   unsigned index = measure_batch->index++;
+   assert(index % 2 == 1);
+
+   iris_emit_pipe_control_write(batch, "measurement snapshot",
+                                PIPE_CONTROL_WRITE_TIMESTAMP |
+                                PIPE_CONTROL_CS_STALL,
+                                batch->measure->bo,
+                                index * sizeof(uint64_t), 0ull);
+
+   struct intel_measure_snapshot *snapshot = &(measure_batch->snapshots[index]);
+   memset(snapshot, 0, sizeof(*snapshot));
+   snapshot->type = INTEL_SNAPSHOT_END;
+   snapshot->event_count = event_count;
+}
+
+static bool
+state_changed(const struct iris_context *ice,
+              const struct iris_batch *batch,
+              enum intel_measure_snapshot_type type)
+{
+   uintptr_t vs=0, tcs=0, tes=0, gs=0, fs=0, cs=0;
+
+   if (type == INTEL_SNAPSHOT_COMPUTE) {
+      cs = (uintptr_t) ice->shaders.prog[MESA_SHADER_COMPUTE];
+   } else if (type == INTEL_SNAPSHOT_DRAW) {
+      vs  = (uintptr_t) ice->shaders.prog[MESA_SHADER_VERTEX];
+      tcs = (uintptr_t) ice->shaders.prog[MESA_SHADER_TESS_CTRL];
+      tes = (uintptr_t) ice->shaders.prog[MESA_SHADER_TESS_EVAL];
+      gs  = (uintptr_t) ice->shaders.prog[MESA_SHADER_GEOMETRY];
+      fs  = (uintptr_t) ice->shaders.prog[MESA_SHADER_FRAGMENT];
+   }
+   /* else blorp, all programs NULL */
+
+   return intel_measure_state_changed(&batch->measure->base,
+                                      vs, tcs, tes, gs, fs, cs);
+}
+
+static void
+iris_measure_renderpass(struct iris_context *ice)
+{
+   const struct intel_measure_config *config = config_from_context(ice);
+   struct intel_measure_batch *batch =
+      &ice->batches[IRIS_BATCH_RENDER].measure->base;
+
+   if (!config)
+      return;
+   uint32_t framebuffer_crc = util_hash_crc32(&ice->state.framebuffer,
+                                              sizeof(ice->state.framebuffer));
+   if (framebuffer_crc == batch->framebuffer)
+      return;
+   bool filtering = config->flags & INTEL_MEASURE_RENDERPASS;
+   if (filtering && batch->index % 2 == 1) {
+      /* snapshot for previous renderpass was not ended */
+      measure_end_snapshot(&ice->batches[IRIS_BATCH_RENDER],
+                           batch->event_count);
+      batch->event_count = 0;
+   }
+
+   batch->framebuffer = framebuffer_crc;
+}
+
+void
+_iris_measure_snapshot(struct iris_context *ice,
+                       struct iris_batch *batch,
+                       enum intel_measure_snapshot_type type,
+                       const struct pipe_draw_info *draw,
+                       const struct pipe_draw_indirect_info *indirect,
+                       const struct pipe_draw_start_count *sc)
+{
+
+   const struct intel_measure_config *config = config_from_context(ice);
+   struct intel_measure_batch* measure_batch = &batch->measure->base;
+
+   assert(config);
+   if (!config->enabled)
+      return;
+   if (measure_batch == NULL)
+      return;
+
+   assert(type != INTEL_SNAPSHOT_END);
+   iris_measure_renderpass(ice);
+
+   if (!state_changed(ice, batch, type)) {
+      /* filter out this event */
+      return;
+   }
+
+   /* increment event count */
+   ++measure_batch->event_count;
+   if (measure_batch->event_count == 1 ||
+       measure_batch->event_count == config->event_interval + 1) {
+      /* the first event of an interval */
+      if (measure_batch->index % 2) {
+         /* end the previous event */
+         measure_end_snapshot(batch, measure_batch->event_count - 1);
+      }
+      measure_batch->event_count = 1;
+
+      const char *event_name = NULL;
+      int count = 0;
+      if (sc)
+         count = sc->count;
+
+      if (draw != NULL) {
+         const struct shader_info *fs_info =
+            iris_get_shader_info(ice, MESA_SHADER_FRAGMENT);
+         if (fs_info && fs_info->name && strncmp(fs_info->name, "st/", 2) == 0) {
+            event_name = fs_info->name;
+         } else if (indirect) {
+            event_name = "DrawIndirect";
+            if (indirect->count_from_stream_output) {
+               event_name = "DrawTransformFeedback";
+            }
+         }
+         else if (draw->index_size)
+            event_name = "DrawElements";
+         else
+            event_name = "DrawArrays";
+         count = count * (draw->instance_count ? draw->instance_count : 1);
+      }
+
+      measure_start_snapshot(ice, batch, type, event_name, count);
+      return;
+   }
+}
+
+static void
+iris_measure_gather(struct iris_context *ice)
+{
+   struct iris_screen *screen = (struct iris_screen *) ice->ctx.screen;
+   struct intel_measure_device *measure_device = &screen->measure;
+
+   /* gather snapshots */
+   pthread_mutex_lock(&measure_device->mutex);
+
+   /* iterate snapshots and collect if ready */
+   while (!list_is_empty(&measure_device->queued_snapshots)) {
+      struct iris_measure_batch *measure =
+         list_first_entry(&measure_device->queued_snapshots,
+                          struct iris_measure_batch, link);
+
+      assert(measure->base.submitted == true);
+      if (!iris_measure_ready(measure)) {
+         /* command buffer has begun execution on the gpu, but has not
+          * completed.
+          */
+         break;
+      }
+
+      /* iris_bo_wait returns immediately if the batch has been submitted but
+       * not started execution.  The first timestamp will be non-zero if the
+       * buffer object is ready.
+       */
+      uint64_t *map = iris_bo_map(NULL, measure->bo, MAP_READ);
+      if (map[0] == 0) {
+         /* The command buffer has not begun execution on the gpu. */
+         iris_bo_unmap(measure->bo);
+         break;
+      }
+
+      list_del(&measure->link);
+      assert(measure->bo);
+      assert(measure->base.index % 2 == 0);
+
+      intel_measure_push_result(measure_device, &measure->base, map);
+
+      iris_bo_unmap(measure->bo);
+      measure->base.index = 0;
+      measure->base.frame = 0;
+      measure->base.submitted = false;
+      iris_destroy_batch_measure(measure);
+   }
+
+   intel_measure_print(measure_device, &screen->devinfo);
+   pthread_mutex_unlock(&measure_device->mutex);
+}
+
+
+void
+iris_destroy_ctx_measure(struct iris_context *ice)
+{
+   /* All outstanding snapshots must be collected before the context is
+    * destroyed.
+    */
+   iris_measure_gather(ice);
+}
+
+void
+iris_measure_batch_end(struct iris_context *ice, struct iris_batch *batch)
+{
+   const struct intel_measure_config *config = config_from_context(ice);
+   struct iris_measure_batch *iris_measure_batch = batch->measure;
+   struct intel_measure_batch *measure_batch = &iris_measure_batch->base;
+   struct intel_measure_device *measure_device =
+      &((struct iris_screen *) ice->ctx.screen)->measure;
+
+   if (!config)
+      return;
+   if (!config->enabled)
+      return;
+
+   assert(measure_batch);
+   assert(measure_device);
+
+   static unsigned batch_count = 0;
+   measure_batch->batch_count = p_atomic_inc_return(&batch_count);
+
+   if (measure_batch->index % 2) {
+      /* We hit the end of the batch, but never terminated our section of
+       * drawing with the same render target or shaders.  End it now.
+       */
+      measure_end_snapshot(batch, measure_batch->event_count);
+   }
+
+   if (measure_batch->index == 0)
+      return;
+
+   /* enqueue snapshot for gathering */
+   pthread_mutex_lock(&measure_device->mutex);
+   list_addtail(&iris_measure_batch->link, &measure_device->queued_snapshots);
+   measure_batch->submitted = true;
+   batch->measure = NULL;
+   pthread_mutex_unlock(&measure_device->mutex);
+   /* init new measure_batch */
+   iris_init_batch_measure(ice, batch);
+
+   static int interval = 0;
+   if (++interval > 10) {
+      iris_measure_gather(ice);
+      interval = 0;
+   }
+}
+
+void
+iris_measure_frame_end(struct iris_context *ice)
+{
+   struct iris_screen *screen = (struct iris_screen *) ice->ctx.screen;
+   struct intel_measure_device *measure_device = &screen->measure;
+   const struct intel_measure_config *config = measure_device->config;
+
+   if (!config)
+      return;
+   /* check that the snapshots were submitted */
+   for (int i = 0; i < IRIS_BATCH_COUNT; ++i) {
+      struct intel_measure_batch *measure_batch =
+         &ice->batches[i].measure->base;
+
+      if (measure_batch != NULL) {
+         assert(measure_batch->submitted || measure_batch->index == 0);
+      }
+   }
+
+   /* increment frame counter */
+   intel_measure_frame_transition(p_atomic_inc_return(&measure_device->frame));
+
+   iris_measure_gather(ice);
+}
diff --git a/src/gallium/drivers/iris/iris_measure.h b/src/gallium/drivers/iris/iris_measure.h
new file mode 100644 (file)
index 0000000..b73f151
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * 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
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) AND/OR THEIR 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.
+ */
+
+#ifndef IRIS_MEASURE_H
+#define IRIS_MEASURE_H
+
+#include "common/intel_measure.h"
+#include "pipe/p_state.h"
+struct iris_screen;
+
+struct iris_measure_batch {
+   struct iris_bo *bo;
+   struct list_head link;
+   struct intel_measure_batch base;
+};
+
+void iris_init_screen_measure(struct iris_screen *screen);
+void iris_init_batch_measure(struct iris_context *ice,
+                             struct iris_batch *batch);
+void iris_destroy_batch_measure(struct iris_measure_batch *batch);
+void iris_destroy_ctx_measure(struct iris_context *ice);
+void iris_destroy_screen_measure(struct iris_screen *screen);
+void iris_measure_frame_end(struct iris_context *ice);
+void iris_measure_batch_end(struct iris_context *ice, struct iris_batch *batch);
+void _iris_measure_snapshot(struct iris_context *ice,
+                            struct iris_batch *batch,
+                            enum intel_measure_snapshot_type type,
+                            const struct pipe_draw_info *draw,
+                            const struct pipe_draw_indirect_info *indirect,
+                            const struct pipe_draw_start_count *sc);
+
+#define iris_measure_snapshot(ice, batch, type, draw, indirect, start_count)        \
+   if (unlikely(((struct iris_screen *) ice->ctx.screen)->measure.config)) \
+      _iris_measure_snapshot(ice, batch, type, draw, indirect, start_count)
+
+#endif  /* IRIS_MEASURE_H */
index 2b9b43b..b9306c2 100644 (file)
@@ -32,6 +32,7 @@
 #include "intel/isl/isl.h"
 #include "iris_bufmgr.h"
 #include "iris_binder.h"
+#include "iris_measure.h"
 #include "iris_resource.h"
 
 struct gen_l3_config;
@@ -218,6 +219,8 @@ struct iris_screen {
    struct iris_address workaround_address;
 
    struct disk_cache *disk_cache;
+
+   struct intel_measure_device measure;
 };
 
 struct pipe_screen *
index 4a616ca..24e3373 100644 (file)
@@ -39,6 +39,8 @@ files_libiris = files(
   'iris_formats.c',
   'iris_genx_macros.h',
   'iris_genx_protos.h',
+  'iris_measure.c',
+  'iris_measure.h',
   'iris_monitor.c',
   'iris_perf.h',
   'iris_perf.c',