From e67b8f504b4cbd1bc951fee4a8f53b19f25d36c8 Mon Sep 17 00:00:00 2001 From: Mark Janes Date: Mon, 11 May 2020 13:51:45 -0700 Subject: [PATCH] iris: implement iris layer of INTEL_MEASURE Acked-by: Kenneth Graunke Part-of: --- src/gallium/drivers/iris/iris_batch.h | 1 + src/gallium/drivers/iris/iris_measure.c | 465 ++++++++++++++++++++++++ src/gallium/drivers/iris/iris_measure.h | 56 +++ src/gallium/drivers/iris/iris_screen.h | 3 + src/gallium/drivers/iris/meson.build | 2 + 5 files changed, 527 insertions(+) create mode 100644 src/gallium/drivers/iris/iris_measure.c create mode 100644 src/gallium/drivers/iris/iris_measure.h diff --git a/src/gallium/drivers/iris/iris_batch.h b/src/gallium/drivers/iris/iris_batch.h index 2f053032dd1..76927ebf3f7 100644 --- a/src/gallium/drivers/iris/iris_batch.h +++ b/src/gallium/drivers/iris/iris_batch.h @@ -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 index 00000000000..9013af0a863 --- /dev/null +++ b/src/gallium/drivers/iris/iris_measure.c @@ -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 +#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 index 00000000000..b73f1519d25 --- /dev/null +++ b/src/gallium/drivers/iris/iris_measure.h @@ -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 */ diff --git a/src/gallium/drivers/iris/iris_screen.h b/src/gallium/drivers/iris/iris_screen.h index 2b9b43b427e..b9306c2d176 100644 --- a/src/gallium/drivers/iris/iris_screen.h +++ b/src/gallium/drivers/iris/iris_screen.h @@ -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 * diff --git a/src/gallium/drivers/iris/meson.build b/src/gallium/drivers/iris/meson.build index 4a616ca8696..24e337389e6 100644 --- a/src/gallium/drivers/iris/meson.build +++ b/src/gallium/drivers/iris/meson.build @@ -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',