freedreno: Re-work fd_submit fence interface
Move everything into a struct assocated with the pipe_fence_handle, so that the drm layer can fill in the seqn/fd fences directly. This will give us a comvenient place to insert a util_queue_fence in the next commit. While we're at it, extract the uint32_t fence (previously called 'timestamp' in place, a kgsl legacy) into a struct that encapsulates both the kernel fence and the userspace fence. Signed-off-by: Rob Clark <robdclark@chromium.org> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/10444>
This commit is contained in:
parent
d531c8d22a
commit
aafcd8aacb
|
@ -284,7 +284,7 @@ main(int argc, char **argv)
|
|||
|
||||
backend->emit_grid(kernel, grid, submit);
|
||||
|
||||
fd_submit_flush(submit, -1, NULL, NULL);
|
||||
fd_submit_flush(submit, -1, NULL);
|
||||
|
||||
for (int i = 0; i < kernel->num_bufs; i++) {
|
||||
fd_bo_cpu_prep(kernel->bufs[i], pipe, FD_BO_PREP_READ);
|
||||
|
|
|
@ -79,6 +79,22 @@ fd_fence_after(uint32_t a, uint32_t b)
|
|||
return (int32_t)(a - b) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per submit, there are actually two fences:
|
||||
* 1) The userspace maintained fence, which is used to optimistically
|
||||
* avoid kernel ioctls to query if specific rendering is completed
|
||||
* 2) The kernel maintained fence, which we cannot directly do anything
|
||||
* with, other than pass it back to the kernel
|
||||
*
|
||||
* The userspace fence is mostly internal to the drm layer, but we want
|
||||
* the gallium layer to be able to pass it back to us for things like
|
||||
* fd_pipe_wait(). So this struct encapsulates the two.
|
||||
*/
|
||||
struct fd_fence {
|
||||
uint32_t kfence; /* kernel fence */
|
||||
uint32_t ufence; /* userspace fence */
|
||||
};
|
||||
|
||||
/* bo flags: */
|
||||
#define FD_BO_GPUREADONLY BITSET_BIT(1)
|
||||
#define FD_BO_SCANOUT BITSET_BIT(2)
|
||||
|
@ -126,9 +142,9 @@ struct fd_pipe *fd_pipe_ref_locked(struct fd_pipe *pipe);
|
|||
void fd_pipe_del(struct fd_pipe *pipe);
|
||||
int fd_pipe_get_param(struct fd_pipe *pipe, enum fd_param_id param,
|
||||
uint64_t *value);
|
||||
int fd_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp);
|
||||
int fd_pipe_wait(struct fd_pipe *pipe, const struct fd_fence *fence);
|
||||
/* timeout in nanosec */
|
||||
int fd_pipe_wait_timeout(struct fd_pipe *pipe, uint32_t timestamp,
|
||||
int fd_pipe_wait_timeout(struct fd_pipe *pipe, const struct fd_fence *fence,
|
||||
uint64_t timeout);
|
||||
|
||||
/* buffer-object functions:
|
||||
|
|
|
@ -124,16 +124,19 @@ fd_pipe_get_param(struct fd_pipe *pipe, enum fd_param_id param, uint64_t *value)
|
|||
}
|
||||
|
||||
int
|
||||
fd_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp)
|
||||
fd_pipe_wait(struct fd_pipe *pipe, const struct fd_fence *fence)
|
||||
{
|
||||
return fd_pipe_wait_timeout(pipe, timestamp, ~0);
|
||||
return fd_pipe_wait_timeout(pipe, fence, ~0);
|
||||
}
|
||||
|
||||
int
|
||||
fd_pipe_wait_timeout(struct fd_pipe *pipe, uint32_t timestamp, uint64_t timeout)
|
||||
fd_pipe_wait_timeout(struct fd_pipe *pipe, const struct fd_fence *fence,
|
||||
uint64_t timeout)
|
||||
{
|
||||
if (!fd_fence_after(fence->ufence, pipe->control->fence))
|
||||
return 0;
|
||||
|
||||
return pipe->funcs->wait(pipe, timestamp, timeout);
|
||||
return pipe->funcs->wait(pipe, fence, timeout);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
|
|
@ -147,7 +147,8 @@ struct fd_pipe_funcs {
|
|||
struct fd_submit *(*submit_new)(struct fd_pipe *pipe);
|
||||
int (*get_param)(struct fd_pipe *pipe, enum fd_param_id param,
|
||||
uint64_t *value);
|
||||
int (*wait)(struct fd_pipe *pipe, uint32_t timestamp, uint64_t timeout);
|
||||
int (*wait)(struct fd_pipe *pipe, const struct fd_fence *fence,
|
||||
uint64_t timeout);
|
||||
void (*destroy)(struct fd_pipe *pipe);
|
||||
};
|
||||
|
||||
|
@ -188,8 +189,8 @@ struct fd_submit_funcs {
|
|||
struct fd_ringbuffer *(*new_ringbuffer)(struct fd_submit *submit,
|
||||
uint32_t size,
|
||||
enum fd_ringbuffer_flags flags);
|
||||
int (*flush)(struct fd_submit *submit, int in_fence_fd, int *out_fence_fd,
|
||||
uint32_t *out_fence);
|
||||
int (*flush)(struct fd_submit *submit, int in_fence_fd,
|
||||
struct fd_submit_fence *out_fence);
|
||||
void (*destroy)(struct fd_submit *submit);
|
||||
};
|
||||
|
||||
|
|
|
@ -58,11 +58,11 @@ fd_submit_ref(struct fd_submit *submit)
|
|||
}
|
||||
|
||||
int
|
||||
fd_submit_flush(struct fd_submit *submit, int in_fence_fd, int *out_fence_fd,
|
||||
uint32_t *out_fence)
|
||||
fd_submit_flush(struct fd_submit *submit, int in_fence_fd,
|
||||
struct fd_submit_fence *out_fence)
|
||||
{
|
||||
submit->fence = fd_pipe_emit_fence(submit->pipe, submit->primary);
|
||||
return submit->funcs->flush(submit, in_fence_fd, out_fence_fd, out_fence);
|
||||
return submit->funcs->flush(submit, in_fence_fd, out_fence);
|
||||
}
|
||||
|
||||
struct fd_ringbuffer *
|
||||
|
|
|
@ -92,11 +92,26 @@ struct fd_ringbuffer *fd_submit_new_ringbuffer(struct fd_submit *submit,
|
|||
uint32_t size,
|
||||
enum fd_ringbuffer_flags flags);
|
||||
|
||||
/**
|
||||
* Encapsulates submit out-fence(s), which consist of a 'timestamp' (per-
|
||||
* pipe (submitqueue) sequence number) and optionally, if requested, an
|
||||
* out-fence-fd
|
||||
*/
|
||||
struct fd_submit_fence {
|
||||
struct fd_fence fence;
|
||||
|
||||
/**
|
||||
* Optional dma_fence fd, returned by submit if use_fence_fd is true
|
||||
*/
|
||||
int fence_fd;
|
||||
bool use_fence_fd;
|
||||
};
|
||||
|
||||
/* in_fence_fd: -1 for no in-fence, else fence fd
|
||||
* out_fence_fd: NULL for no output-fence requested, else ptr to return out-fence
|
||||
* out_fence can be NULL if no output fence is required
|
||||
*/
|
||||
int fd_submit_flush(struct fd_submit *submit, int in_fence_fd,
|
||||
int *out_fence_fd, uint32_t *out_fence);
|
||||
struct fd_submit_fence *out_fence);
|
||||
|
||||
struct fd_ringbuffer;
|
||||
struct fd_reloc;
|
||||
|
|
|
@ -106,11 +106,11 @@ msm_pipe_get_param(struct fd_pipe *pipe, enum fd_param_id param,
|
|||
}
|
||||
|
||||
static int
|
||||
msm_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp, uint64_t timeout)
|
||||
msm_pipe_wait(struct fd_pipe *pipe, const struct fd_fence *fence, uint64_t timeout)
|
||||
{
|
||||
struct fd_device *dev = pipe->dev;
|
||||
struct drm_msm_wait_fence req = {
|
||||
.fence = timestamp,
|
||||
.fence = fence->kfence,
|
||||
.queueid = to_msm_pipe(pipe)->queue_id,
|
||||
};
|
||||
int ret;
|
||||
|
|
|
@ -267,8 +267,8 @@ handle_stateobj_relocs(struct msm_submit *submit, struct msm_ringbuffer *ring)
|
|||
}
|
||||
|
||||
static int
|
||||
msm_submit_flush(struct fd_submit *submit, int in_fence_fd, int *out_fence_fd,
|
||||
uint32_t *out_fence)
|
||||
msm_submit_flush(struct fd_submit *submit, int in_fence_fd,
|
||||
struct fd_submit_fence *out_fence)
|
||||
{
|
||||
struct msm_submit *msm_submit = to_msm_submit(submit);
|
||||
struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);
|
||||
|
@ -354,7 +354,7 @@ msm_submit_flush(struct fd_submit *submit, int in_fence_fd, int *out_fence_fd,
|
|||
req.fence_fd = in_fence_fd;
|
||||
}
|
||||
|
||||
if (out_fence_fd) {
|
||||
if (out_fence && out_fence->use_fence_fd) {
|
||||
req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
|
||||
}
|
||||
|
||||
|
@ -370,12 +370,10 @@ msm_submit_flush(struct fd_submit *submit, int in_fence_fd, int *out_fence_fd,
|
|||
if (ret) {
|
||||
ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
|
||||
msm_dump_submit(&req);
|
||||
} else if (!ret) {
|
||||
if (out_fence)
|
||||
*out_fence = req.fence;
|
||||
|
||||
if (out_fence_fd)
|
||||
*out_fence_fd = req.fence_fd;
|
||||
} else if (!ret && out_fence) {
|
||||
out_fence->fence.kfence = req.fence;
|
||||
out_fence->fence.ufence = submit->fence;
|
||||
out_fence->fence_fd = req.fence_fd;
|
||||
}
|
||||
|
||||
for (unsigned o = 0; o < nr_objs; o++)
|
||||
|
|
|
@ -206,7 +206,7 @@ msm_submit_sp_new_ringbuffer(struct fd_submit *submit, uint32_t size,
|
|||
|
||||
static int
|
||||
msm_submit_sp_flush(struct fd_submit *submit, int in_fence_fd,
|
||||
int *out_fence_fd, uint32_t *out_fence)
|
||||
struct fd_submit_fence *out_fence)
|
||||
{
|
||||
struct msm_submit_sp *msm_submit = to_msm_submit_sp(submit);
|
||||
struct msm_pipe *msm_pipe = to_msm_pipe(submit->pipe);
|
||||
|
@ -237,7 +237,7 @@ msm_submit_sp_flush(struct fd_submit *submit, int in_fence_fd,
|
|||
req.fence_fd = in_fence_fd;
|
||||
}
|
||||
|
||||
if (out_fence_fd) {
|
||||
if (out_fence && out_fence->use_fence_fd) {
|
||||
req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
|
||||
}
|
||||
|
||||
|
@ -276,12 +276,10 @@ msm_submit_sp_flush(struct fd_submit *submit, int in_fence_fd,
|
|||
if (ret) {
|
||||
ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
|
||||
msm_dump_submit(&req);
|
||||
} else if (!ret) {
|
||||
if (out_fence)
|
||||
*out_fence = req.fence;
|
||||
|
||||
if (out_fence_fd)
|
||||
*out_fence_fd = req.fence_fd;
|
||||
} else if (!ret && out_fence) {
|
||||
out_fence->fence.kfence = req.fence;
|
||||
out_fence->fence.ufence = submit->fence;
|
||||
out_fence->fence_fd = req.fence_fd;
|
||||
}
|
||||
|
||||
if (!bos_on_stack)
|
||||
|
|
|
@ -173,7 +173,7 @@ flush_ring(void)
|
|||
if (!dev.submit)
|
||||
return;
|
||||
|
||||
ret = fd_submit_flush(dev.submit, -1, NULL, NULL);
|
||||
ret = fd_submit_flush(dev.submit, -1, NULL);
|
||||
if (ret)
|
||||
errx(1, "submit failed: %d", ret);
|
||||
fd_ringbuffer_del(dev.ring);
|
||||
|
|
|
@ -205,7 +205,7 @@ batch_fini(struct fd_batch *batch)
|
|||
|
||||
/* in case batch wasn't flushed but fence was created: */
|
||||
if (batch->fence)
|
||||
fd_fence_populate(batch->fence, 0, -1);
|
||||
fd_fence_set_batch(batch->fence, NULL);
|
||||
|
||||
fd_fence_ref(&batch->fence, NULL);
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ struct fd_batch {
|
|||
uint32_t *last_timestamp_cmd;
|
||||
|
||||
int in_fence_fd;
|
||||
bool needs_out_fence_fd;
|
||||
struct pipe_fence_handle *fence;
|
||||
|
||||
struct fd_context *ctx;
|
||||
|
|
|
@ -117,7 +117,7 @@ fd_context_flush(struct pipe_context *pctx, struct pipe_fence_handle **fencep,
|
|||
fd_fence_ref(&fence, batch->fence);
|
||||
|
||||
if (flags & PIPE_FLUSH_FENCE_FD)
|
||||
batch->needs_out_fence_fd = true;
|
||||
fence->submit_fence.use_fence_fd = true;
|
||||
|
||||
fd_bc_dump(ctx->screen, "%p: flushing %p<%u>, flags=0x%x, pending:\n", ctx,
|
||||
batch, batch->seqno, flags);
|
||||
|
|
|
@ -59,8 +59,8 @@ fence_flush(struct pipe_context *pctx, struct pipe_fence_handle *fence,
|
|||
}
|
||||
}
|
||||
|
||||
/* We've already waited for batch to be flushed and fd_fence_populate()
|
||||
* called:
|
||||
/* We've already waited for batch to be flushed and fence->batch
|
||||
* to be cleared:
|
||||
*/
|
||||
assert(!fence->batch);
|
||||
return true;
|
||||
|
@ -74,35 +74,23 @@ fence_flush(struct pipe_context *pctx, struct pipe_fence_handle *fence,
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
fd_fence_populate(struct pipe_fence_handle *fence, uint32_t timestamp,
|
||||
int fence_fd)
|
||||
{
|
||||
if (!fence->batch)
|
||||
return;
|
||||
fence->timestamp = timestamp;
|
||||
fence->fence_fd = fence_fd;
|
||||
fence->batch = NULL;
|
||||
|
||||
if (fence->needs_signal) {
|
||||
util_queue_fence_signal(&fence->ready);
|
||||
fence->needs_signal = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fd_fence_repopulate(struct pipe_fence_handle *fence, struct pipe_fence_handle *last_fence)
|
||||
{
|
||||
int fence_fd = (last_fence->fence_fd == -1) ? -1 : dup(last_fence->fence_fd);
|
||||
fd_fence_populate(fence, last_fence->timestamp, fence_fd);
|
||||
/* The fence we are re-populating must not be an fd-fence (but last_fince
|
||||
* might have been)
|
||||
*/
|
||||
assert(!fence->submit_fence.use_fence_fd);
|
||||
|
||||
fence->submit_fence.fence = last_fence->submit_fence.fence;
|
||||
}
|
||||
|
||||
static void
|
||||
fd_fence_destroy(struct pipe_fence_handle *fence)
|
||||
{
|
||||
tc_unflushed_batch_token_reference(&fence->tc_token, NULL);
|
||||
if (fence->fence_fd != -1)
|
||||
close(fence->fence_fd);
|
||||
if (fence->submit_fence.use_fence_fd)
|
||||
close(fence->submit_fence.fence_fd);
|
||||
if (fence->syncobj)
|
||||
drmSyncobjDestroy(fd_device_fd(fence->screen->dev), fence->syncobj);
|
||||
fd_pipe_del(fence->pipe);
|
||||
|
@ -125,12 +113,12 @@ fd_fence_finish(struct pipe_screen *pscreen, struct pipe_context *pctx,
|
|||
if (!fence_flush(pctx, fence, timeout))
|
||||
return false;
|
||||
|
||||
if (fence->fence_fd != -1) {
|
||||
int ret = sync_wait(fence->fence_fd, timeout / 1000000);
|
||||
if (fence->submit_fence.use_fence_fd) {
|
||||
int ret = sync_wait(fence->submit_fence.fence_fd, timeout / 1000000);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
if (fd_pipe_wait_timeout(fence->pipe, fence->timestamp, timeout))
|
||||
if (fd_pipe_wait_timeout(fence->pipe, &fence->submit_fence.fence, timeout))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -153,7 +141,8 @@ fence_create(struct fd_context *ctx, struct fd_batch *batch, int fence_fd,
|
|||
fence->batch = batch;
|
||||
fence->pipe = fd_pipe_ref(ctx->pipe);
|
||||
fence->screen = ctx->screen;
|
||||
fence->fence_fd = fence_fd;
|
||||
fence->submit_fence.fence_fd = fence_fd;
|
||||
fence->submit_fence.use_fence_fd = (fence_fd != -1);
|
||||
fence->syncobj = syncobj;
|
||||
|
||||
return fence;
|
||||
|
@ -198,10 +187,10 @@ fd_fence_server_sync(struct pipe_context *pctx, struct pipe_fence_handle *fence)
|
|||
fence_flush(pctx, fence, 0);
|
||||
|
||||
/* if not an external fence, then nothing more to do without preemption: */
|
||||
if (fence->fence_fd == -1)
|
||||
if (!fence->submit_fence.use_fence_fd)
|
||||
return;
|
||||
|
||||
if (sync_accumulate("freedreno", &ctx->in_fence_fd, fence->fence_fd)) {
|
||||
if (sync_accumulate("freedreno", &ctx->in_fence_fd, fence->submit_fence.fence_fd)) {
|
||||
/* error */
|
||||
}
|
||||
}
|
||||
|
@ -220,18 +209,20 @@ fd_fence_server_signal(struct pipe_context *pctx,
|
|||
int
|
||||
fd_fence_get_fd(struct pipe_screen *pscreen, struct pipe_fence_handle *fence)
|
||||
{
|
||||
assert(fence->submit_fence.use_fence_fd);
|
||||
|
||||
/* NOTE: in the deferred fence case, the pctx we want is the threaded-ctx
|
||||
* but if TC is not used, this will be null. Which is fine, we won't call
|
||||
* threaded_context_flush() in that case
|
||||
*/
|
||||
fence_flush(&fence->ctx->tc->base, fence, PIPE_TIMEOUT_INFINITE);
|
||||
return os_dupfd_cloexec(fence->fence_fd);
|
||||
return os_dupfd_cloexec(fence->submit_fence.fence_fd);
|
||||
}
|
||||
|
||||
bool
|
||||
fd_fence_is_fd(struct pipe_fence_handle *fence)
|
||||
{
|
||||
return fence->fence_fd != -1;
|
||||
return fence->submit_fence.use_fence_fd;
|
||||
}
|
||||
|
||||
struct pipe_fence_handle *
|
||||
|
@ -243,8 +234,20 @@ fd_fence_create(struct fd_batch *batch)
|
|||
void
|
||||
fd_fence_set_batch(struct pipe_fence_handle *fence, struct fd_batch *batch)
|
||||
{
|
||||
assert(!fence->batch);
|
||||
fence->batch = batch;
|
||||
if (batch) {
|
||||
assert(!fence->batch);
|
||||
fence->batch = batch;
|
||||
} else {
|
||||
fence->batch = NULL;
|
||||
|
||||
/* When the batch is dis-associated with the fence, we can signal TC
|
||||
* that the fence is flushed
|
||||
*/
|
||||
if (fence->needs_signal) {
|
||||
util_queue_fence_signal(&fence->ready);
|
||||
fence->needs_signal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct pipe_fence_handle *
|
||||
|
|
|
@ -30,12 +30,15 @@
|
|||
#include "pipe/p_context.h"
|
||||
#include "util/u_queue.h"
|
||||
|
||||
#include "drm/freedreno_ringbuffer.h"
|
||||
|
||||
struct pipe_fence_handle {
|
||||
struct pipe_reference reference;
|
||||
|
||||
/* fence holds a weak reference to the batch until the batch is flushed,
|
||||
* at which point fd_fence_populate() is called and timestamp and possibly
|
||||
* fence_fd become valid and the week reference is dropped.
|
||||
/* fence holds a weak reference to the batch until the batch is flushed, to
|
||||
* accommodate PIPE_FLUSH_DEFERRED. When the batch is actually flushed, it
|
||||
* is cleared (before the batch reference is dropped). If we need to wait
|
||||
* on a fence, and the batch is not NULL, we need to flush it.
|
||||
*
|
||||
* Note that with u_threaded_context async flushes, if a fence is requested
|
||||
* by the frontend, the fence is initially created without a weak reference
|
||||
|
@ -48,8 +51,8 @@ struct pipe_fence_handle {
|
|||
struct tc_unflushed_batch_token *tc_token;
|
||||
bool needs_signal;
|
||||
|
||||
/* For threaded_context async flushes, we must wait on the fence, signalled
|
||||
* in fd_fence_populate(), to know that the rendering has been actually
|
||||
/* For threaded_context async flushes, we must wait on the fence, signaled
|
||||
* when fence->batch is cleared, to know that the rendering has been actually
|
||||
* flushed from the driver thread.
|
||||
*
|
||||
* The ready fence is created signaled for non-async-flush fences, and only
|
||||
|
@ -64,13 +67,10 @@ struct pipe_fence_handle {
|
|||
struct fd_context *ctx;
|
||||
struct fd_pipe *pipe;
|
||||
struct fd_screen *screen;
|
||||
int fence_fd;
|
||||
uint32_t timestamp;
|
||||
struct fd_submit_fence submit_fence;
|
||||
uint32_t syncobj;
|
||||
};
|
||||
|
||||
void fd_fence_populate(struct pipe_fence_handle *fence, uint32_t timestamp,
|
||||
int fence_fd);
|
||||
void fd_fence_repopulate(struct pipe_fence_handle *fence,
|
||||
struct pipe_fence_handle *last_fence);
|
||||
void fd_fence_ref(struct pipe_fence_handle **ptr,
|
||||
|
|
|
@ -654,18 +654,14 @@ render_sysmem(struct fd_batch *batch) assert_dt
|
|||
static void
|
||||
flush_ring(struct fd_batch *batch)
|
||||
{
|
||||
uint32_t timestamp = 0;
|
||||
int out_fence_fd = -1;
|
||||
|
||||
if (FD_DBG(NOHW))
|
||||
return;
|
||||
|
||||
fd_submit_flush(batch->submit, batch->in_fence_fd,
|
||||
batch->needs_out_fence_fd ? &out_fence_fd : NULL,
|
||||
batch->fence ? ×tamp : NULL);
|
||||
batch->fence ? &batch->fence->submit_fence : NULL);
|
||||
|
||||
if (batch->fence)
|
||||
fd_fence_populate(batch->fence, timestamp, out_fence_fd);
|
||||
fd_fence_set_batch(batch->fence, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in New Issue