Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4716185 authored by Chris Wilson's avatar Chris Wilson Committed by Daniel Vetter
Browse files

drm/i915: Implement inter-engine read-read optimisations



Currently, we only track the last request globally across all engines.
This prevents us from issuing concurrent read requests on e.g. the RCS
and BCS engines (or more likely the render and media engines). Without
semaphores, we incur costly stalls as we synchronise between rings -
greatly impacting the current performance of Broadwell versus Haswell in
certain workloads (like video decode). With the introduction of
reference counted requests, it is much easier to track the last request
per ring, as well as the last global write request so that we can
optimise inter-engine read read requests (as well as better optimise
certain CPU waits).

v2: Fix inverted readonly condition for nonblocking waits.
v3: Handle non-continguous engine array after waits
v4: Rebase, tidy, rewrite ring list debugging
v5: Use obj->active as a bitfield, it looks cool
v6: Micro-optimise, mostly involving moving code around
v7: Fix retire-requests-upto for execlists (and multiple rq->ringbuf)
v8: Rebase
v9: Refactor i915_gem_object_sync() to allow the compiler to better
optimise it.

Benchmark: igt/gem_read_read_speed
hsw:gt3e (with semaphores):
Before: Time to read-read 1024k:		275.794µs
After:  Time to read-read 1024k:		123.260µs

hsw:gt3e (w/o semaphores):
Before: Time to read-read 1024k:		230.433µs
After:  Time to read-read 1024k:		124.593µs

bdw-u (w/o semaphores):             Before          After
Time to read-read 1x1:            26.274µs       10.350µs
Time to read-read 128x128:        40.097µs       21.366µs
Time to read-read 256x256:        77.087µs       42.608µs
Time to read-read 512x512:       281.999µs      181.155µs
Time to read-read 1024x1024:    1196.141µs     1118.223µs
Time to read-read 2048x2048:    5639.072µs     5225.837µs
Time to read-read 4096x4096:   22401.662µs    21137.067µs
Time to read-read 8192x8192:   89617.735µs    85637.681µs

Testcase: igt/gem_concurrent_blit (read-read and friends)
Cc: Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com> [v8]
[danvet: s/\<rq\>/req/g]
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent eed29a5b
Loading
Loading
Loading
Loading
+11 −5
Original line number Original line Diff line number Diff line
@@ -120,10 +120,13 @@ static inline const char *get_global_flag(struct drm_i915_gem_object *obj)
static void
static void
describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
{
{
	struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
	struct intel_engine_cs *ring;
	struct i915_vma *vma;
	struct i915_vma *vma;
	int pin_count = 0;
	int pin_count = 0;
	int i;


	seq_printf(m, "%pK: %s%s%s%s %8zdKiB %02x %02x %x %x %x%s%s%s",
	seq_printf(m, "%pK: %s%s%s%s %8zdKiB %02x %02x [ ",
		   &obj->base,
		   &obj->base,
		   obj->active ? "*" : " ",
		   obj->active ? "*" : " ",
		   get_pin_flag(obj),
		   get_pin_flag(obj),
@@ -131,8 +134,11 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
		   get_global_flag(obj),
		   get_global_flag(obj),
		   obj->base.size / 1024,
		   obj->base.size / 1024,
		   obj->base.read_domains,
		   obj->base.read_domains,
		   obj->base.write_domain,
		   obj->base.write_domain);
		   i915_gem_request_get_seqno(obj->last_read_req),
	for_each_ring(ring, dev_priv, i)
		seq_printf(m, "%x ",
				i915_gem_request_get_seqno(obj->last_read_req[i]));
	seq_printf(m, "] %x %x%s%s%s",
		   i915_gem_request_get_seqno(obj->last_write_req),
		   i915_gem_request_get_seqno(obj->last_write_req),
		   i915_gem_request_get_seqno(obj->last_fenced_req),
		   i915_gem_request_get_seqno(obj->last_fenced_req),
		   i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level),
		   i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level),
@@ -169,9 +175,9 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
		*t = '\0';
		*t = '\0';
		seq_printf(m, " (%s mappable)", s);
		seq_printf(m, " (%s mappable)", s);
	}
	}
	if (obj->last_read_req != NULL)
	if (obj->last_write_req != NULL)
		seq_printf(m, " (%s)",
		seq_printf(m, " (%s)",
			   i915_gem_request_get_ring(obj->last_read_req)->name);
			   i915_gem_request_get_ring(obj->last_write_req)->name);
	if (obj->frontbuffer_bits)
	if (obj->frontbuffer_bits)
		seq_printf(m, " (frontbuffer: 0x%03x)", obj->frontbuffer_bits);
		seq_printf(m, " (frontbuffer: 0x%03x)", obj->frontbuffer_bits);
}
}
+14 −5
Original line number Original line Diff line number Diff line
@@ -508,7 +508,7 @@ struct drm_i915_error_state {
	struct drm_i915_error_buffer {
	struct drm_i915_error_buffer {
		u32 size;
		u32 size;
		u32 name;
		u32 name;
		u32 rseqno, wseqno;
		u32 rseqno[I915_NUM_RINGS], wseqno;
		u32 gtt_offset;
		u32 gtt_offset;
		u32 read_domains;
		u32 read_domains;
		u32 write_domain;
		u32 write_domain;
@@ -1939,7 +1939,7 @@ struct drm_i915_gem_object {
	struct drm_mm_node *stolen;
	struct drm_mm_node *stolen;
	struct list_head global_list;
	struct list_head global_list;


	struct list_head ring_list;
	struct list_head ring_list[I915_NUM_RINGS];
	/** Used in execbuf to temporarily hold a ref */
	/** Used in execbuf to temporarily hold a ref */
	struct list_head obj_exec_link;
	struct list_head obj_exec_link;


@@ -1950,7 +1950,7 @@ struct drm_i915_gem_object {
	 * rendering and so a non-zero seqno), and is not set if it i s on
	 * rendering and so a non-zero seqno), and is not set if it i s on
	 * inactive (ready to be unbound) list.
	 * inactive (ready to be unbound) list.
	 */
	 */
	unsigned int active:1;
	unsigned int active:I915_NUM_RINGS;


	/**
	/**
	 * This is set if the object has been written to since last bound
	 * This is set if the object has been written to since last bound
@@ -2021,8 +2021,17 @@ struct drm_i915_gem_object {
	void *dma_buf_vmapping;
	void *dma_buf_vmapping;
	int vmapping_count;
	int vmapping_count;


	/** Breadcrumb of last rendering to the buffer. */
	/** Breadcrumb of last rendering to the buffer.
	struct drm_i915_gem_request *last_read_req;
	 * There can only be one writer, but we allow for multiple readers.
	 * If there is a writer that necessarily implies that all other
	 * read requests are complete - but we may only be lazily clearing
	 * the read requests. A read request is naturally the most recent
	 * request on a ring, so we may have two different write and read
	 * requests on one ring where the write request is older than the
	 * read request. This allows for the CPU to read from an active
	 * buffer by only waiting for the write to complete.
	 * */
	struct drm_i915_gem_request *last_read_req[I915_NUM_RINGS];
	struct drm_i915_gem_request *last_write_req;
	struct drm_i915_gem_request *last_write_req;
	/** Breadcrumb of last fenced GPU access to the buffer. */
	/** Breadcrumb of last fenced GPU access to the buffer. */
	struct drm_i915_gem_request *last_fenced_req;
	struct drm_i915_gem_request *last_fenced_req;
+331 −209

File changed.

Preview size limit exceeded, changes collapsed.

+0 −2
Original line number Original line Diff line number Diff line
@@ -753,8 +753,6 @@ static int do_switch(struct intel_engine_cs *ring,
		 * swapped, but there is no way to do that yet.
		 * swapped, but there is no way to do that yet.
		 */
		 */
		from->legacy_hw_ctx.rcs_state->dirty = 1;
		from->legacy_hw_ctx.rcs_state->dirty = 1;
		BUG_ON(i915_gem_request_get_ring(
			from->legacy_hw_ctx.rcs_state->last_read_req) != ring);


		/* obj is kept alive until the next request by its active ref */
		/* obj is kept alive until the next request by its active ref */
		i915_gem_object_ggtt_unpin(from->legacy_hw_ctx.rcs_state);
		i915_gem_object_ggtt_unpin(from->legacy_hw_ctx.rcs_state);
+22 −70

File changed.

Preview size limit exceeded, changes collapsed.

Loading