76 lines
3.1 KiB
Text
76 lines
3.1 KiB
Text
|
Motivation:
|
|||
|
|
|||
|
In complicated DMA pipelines such as graphics (multimedia, camera, gpu, display)
|
|||
|
a consumer of a buffer needs to know when the producer has finished producing
|
|||
|
it. Likewise the producer needs to know when the consumer is finished with the
|
|||
|
buffer so it can reuse it. A particular buffer may be consumed by multiple
|
|||
|
consumers which will retain the buffer for different amounts of time. In
|
|||
|
addition, a consumer may consume multiple buffers atomically.
|
|||
|
The sync framework adds an API which allows synchronization between the
|
|||
|
producers and consumers in a generic way while also allowing platforms which
|
|||
|
have shared hardware synchronization primitives to exploit them.
|
|||
|
|
|||
|
Goals:
|
|||
|
* provide a generic API for expressing synchronization dependencies
|
|||
|
* allow drivers to exploit hardware synchronization between hardware
|
|||
|
blocks
|
|||
|
* provide a userspace API that allows a compositor to manage
|
|||
|
dependencies.
|
|||
|
* provide rich telemetry data to allow debugging slowdowns and stalls of
|
|||
|
the graphics pipeline.
|
|||
|
|
|||
|
Objects:
|
|||
|
* sync_timeline
|
|||
|
* sync_pt
|
|||
|
* sync_fence
|
|||
|
|
|||
|
sync_timeline:
|
|||
|
|
|||
|
A sync_timeline is an abstract monotonically increasing counter. In general,
|
|||
|
each driver/hardware block context will have one of these. They can be backed
|
|||
|
by the appropriate hardware or rely on the generic sw_sync implementation.
|
|||
|
Timelines are only ever created through their specific implementations
|
|||
|
(i.e. sw_sync.)
|
|||
|
|
|||
|
sync_pt:
|
|||
|
|
|||
|
A sync_pt is an abstract value which marks a point on a sync_timeline. Sync_pts
|
|||
|
have a single timeline parent. They have 3 states: active, signaled, and error.
|
|||
|
They start in active state and transition, once, to either signaled (when the
|
|||
|
timeline counter advances beyond the sync_pt’s value) or error state.
|
|||
|
|
|||
|
sync_fence:
|
|||
|
|
|||
|
Sync_fences are the primary primitives used by drivers to coordinate
|
|||
|
synchronization of their buffers. They are a collection of sync_pts which may
|
|||
|
or may not have the same timeline parent. A sync_pt can only exist in one fence
|
|||
|
and the fence's list of sync_pts is immutable once created. Fences can be
|
|||
|
waited on synchronously or asynchronously. Two fences can also be merged to
|
|||
|
create a third fence containing a copy of the two fences’ sync_pts. Fences are
|
|||
|
backed by file descriptors to allow userspace to coordinate the display pipeline
|
|||
|
dependencies.
|
|||
|
|
|||
|
Use:
|
|||
|
|
|||
|
A driver implementing sync support should have a work submission function which:
|
|||
|
* takes a fence argument specifying when to begin work
|
|||
|
* asynchronously queues that work to kick off when the fence is signaled
|
|||
|
* returns a fence to indicate when its work will be done.
|
|||
|
* signals the returned fence once the work is completed.
|
|||
|
|
|||
|
Consider an imaginary display driver that has the following API:
|
|||
|
/*
|
|||
|
* assumes buf is ready to be displayed.
|
|||
|
* blocks until the buffer is on screen.
|
|||
|
*/
|
|||
|
void display_buffer(struct dma_buf *buf);
|
|||
|
|
|||
|
The new API will become:
|
|||
|
/*
|
|||
|
* will display buf when fence is signaled.
|
|||
|
* returns immediately with a fence that will signal when buf
|
|||
|
* is no longer displayed.
|
|||
|
*/
|
|||
|
struct sync_fence* display_buffer(struct dma_buf *buf,
|
|||
|
struct sync_fence *fence);
|