--- /dev/null
+/*
+ * 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);
+}