| 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); |