diff --git a/libdrm/nouveau/nouveau_bo.c b/libdrm/nouveau/nouveau_bo.c index b7e6d86..85fc14f 100644 --- a/libdrm/nouveau/nouveau_bo.c +++ b/libdrm/nouveau/nouveau_bo.c @@ -607,6 +607,7 @@ nouveau_bo_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo) pbbo = nvpb->buffers + nvpb->nr_buffers++; nvbo->pending = pbbo; nvbo->pending_channel = chan; + nvbo->pending_refcnt = 0; nouveau_bo_ref(bo, &ref); pbbo->user_priv = (uint64_t)(unsigned long)ref; diff --git a/libdrm/nouveau/nouveau_private.h b/libdrm/nouveau/nouveau_private.h index 9ce87fb..784afc9 100644 --- a/libdrm/nouveau/nouveau_private.h +++ b/libdrm/nouveau/nouveau_private.h @@ -52,6 +52,9 @@ struct nouveau_pushbuf_priv { unsigned *pushbuf; unsigned size; + unsigned marker; + unsigned marker_relocs; + struct drm_nouveau_gem_pushbuf_bo *buffers; unsigned nr_buffers; struct drm_nouveau_gem_pushbuf_reloc *relocs; @@ -99,6 +102,7 @@ struct nouveau_bo_priv { /* Tracking */ struct drm_nouveau_gem_pushbuf_bo *pending; struct nouveau_channel *pending_channel; + int pending_refcnt; int write_marker; /* Userspace object */ diff --git a/libdrm/nouveau/nouveau_pushbuf.c b/libdrm/nouveau/nouveau_pushbuf.c index d434a5f..90250c0 100644 --- a/libdrm/nouveau/nouveau_pushbuf.c +++ b/libdrm/nouveau/nouveau_pushbuf.c @@ -60,17 +60,17 @@ nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr, uint32_t flags, uint32_t vor, uint32_t tor) { struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf); + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); struct drm_nouveau_gem_pushbuf_reloc *r; struct drm_nouveau_gem_pushbuf_bo *pbbo; uint32_t domains = 0; if (nvpb->nr_relocs >= NOUVEAU_GEM_MAX_RELOCS) { fprintf(stderr, "too many relocs!!\n"); - assert(0); return -ENOMEM; } - if (nouveau_bo(bo)->user && (flags & NOUVEAU_BO_WR)) { + if (nvbo->user && (flags & NOUVEAU_BO_WR)) { fprintf(stderr, "write to user buffer!!\n"); return -EINVAL; } @@ -78,16 +78,21 @@ nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr, pbbo = nouveau_bo_emit_buffer(chan, bo); if (!pbbo) { fprintf(stderr, "buffer emit fail :(\n"); - assert(0); return -ENOMEM; } + nvbo->pending_refcnt++; + if (flags & NOUVEAU_BO_VRAM) domains |= NOUVEAU_GEM_DOMAIN_VRAM; if (flags & NOUVEAU_BO_GART) domains |= NOUVEAU_GEM_DOMAIN_GART; + + if (!(pbbo->valid_domains & domains)) { + fprintf(stderr, "no valid domains remain!\n"); + return -EINVAL; + } pbbo->valid_domains &= domains; - assert(pbbo->valid_domains); assert(flags & NOUVEAU_BO_RDWR); if (flags & NOUVEAU_BO_RD) { @@ -95,7 +100,7 @@ nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr, } if (flags & NOUVEAU_BO_WR) { pbbo->write_domains |= domains; - nouveau_bo(bo)->write_marker = 1; + nvbo->write_marker = 1; } r = nvpb->relocs + nvpb->nr_relocs++; @@ -322,18 +327,25 @@ restart_push: /* Update presumed offset/domain for any buffers that moved. * Dereference all buffers on validate list */ - for (i = 0; i < nvpb->nr_buffers; i++) { - struct drm_nouveau_gem_pushbuf_bo *pbbo = &nvpb->buffers[i]; + for (i = 0; i < nvpb->nr_relocs; i++) { + struct drm_nouveau_gem_pushbuf_reloc *r = &nvpb->relocs[i]; + struct drm_nouveau_gem_pushbuf_bo *pbbo = + &nvpb->buffers[r->bo_index]; struct nouveau_bo *bo = (void *)(unsigned long)pbbo->user_priv; + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + + if (--nvbo->pending_refcnt) + continue; if (pbbo->presumed_ok == 0) { - nouveau_bo(bo)->domain = pbbo->presumed_domain; - nouveau_bo(bo)->offset = pbbo->presumed_offset; + nvbo->domain = pbbo->presumed_domain; + nvbo->offset = pbbo->presumed_offset; } - nouveau_bo(bo)->pending = NULL; + nvbo->pending = NULL; nouveau_bo_ref(NULL, &bo); } + nvpb->nr_buffers = 0; nvpb->nr_relocs = 0; @@ -343,6 +355,57 @@ restart_push: if (chan->flush_notify) chan->flush_notify(chan); + nvpb->marker = 0; return ret; } +int +nouveau_pushbuf_marker_emit(struct nouveau_channel *chan, + unsigned wait_dwords, unsigned wait_relocs) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf); + + if (AVAIL_RING(chan) < wait_dwords) + return nouveau_pushbuf_flush(chan, wait_dwords); + + if (nvpb->nr_relocs + wait_relocs >= NOUVEAU_GEM_MAX_RELOCS) + return nouveau_pushbuf_flush(chan, wait_dwords); + + nvpb->marker = nvpb->base.cur - nvpb->pushbuf; + nvpb->marker_relocs = nvpb->nr_relocs; + return 0; +} + +void +nouveau_pushbuf_marker_undo(struct nouveau_channel *chan) +{ + struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf); + unsigned i; + + if (!nvpb->marker) + return; + + /* undo any relocs/buffers added to the list since last marker */ + for (i = nvpb->marker_relocs; i < nvpb->nr_relocs; i++) { + struct drm_nouveau_gem_pushbuf_reloc *r = &nvpb->relocs[i]; + struct drm_nouveau_gem_pushbuf_bo *pbbo = + &nvpb->buffers[r->bo_index]; + struct nouveau_bo *bo = (void *)(unsigned long)pbbo->user_priv; + struct nouveau_bo_priv *nvbo = nouveau_bo(bo); + + if (--nvbo->pending_refcnt) + continue; + + nvbo->pending = NULL; + nouveau_bo_ref(NULL, &bo); + nvpb->nr_buffers--; + } + nvpb->nr_relocs = nvpb->marker_relocs; + + /* reset pushbuf back to last marker */ + nvpb->base.cur = nvpb->pushbuf + nvpb->marker; + nvpb->base.remaining = nvpb->size - nvpb->marker; + nvpb->marker = 0; +} + + diff --git a/libdrm/nouveau/nouveau_pushbuf.h b/libdrm/nouveau/nouveau_pushbuf.h index 3c746ed..c7ac8c4 100644 --- a/libdrm/nouveau/nouveau_pushbuf.h +++ b/libdrm/nouveau/nouveau_pushbuf.h @@ -40,11 +40,30 @@ int nouveau_pushbuf_flush(struct nouveau_channel *, unsigned min); int +nouveau_pushbuf_marker_emit(struct nouveau_channel *chan, + unsigned wait_dwords, unsigned wait_relocs); + +void +nouveau_pushbuf_marker_undo(struct nouveau_channel *chan); + +int nouveau_pushbuf_emit_reloc(struct nouveau_channel *, void *ptr, struct nouveau_bo *, uint32_t data, uint32_t data2, uint32_t flags, uint32_t vor, uint32_t tor); /* Push buffer access macros */ +static __inline__ int +MARK_RING(struct nouveau_channel *chan, unsigned dwords, unsigned relocs) +{ + return nouveau_pushbuf_marker_emit(chan, dwords, relocs); +} + +static __inline__ void +MARK_UNDO(struct nouveau_channel *chan) +{ + nouveau_pushbuf_marker_undo(chan); +} + static __inline__ void OUT_RING(struct nouveau_channel *chan, unsigned data) { @@ -116,62 +135,62 @@ BIND_RING(struct nouveau_channel *chan, struct nouveau_grobj *gr, unsigned sc) OUT_RING (chan, gr->handle); } -static __inline__ void +static __inline__ int OUT_RELOC(struct nouveau_channel *chan, struct nouveau_bo *bo, unsigned data, unsigned flags, unsigned vor, unsigned tor) { - nouveau_pushbuf_emit_reloc(chan, chan->pushbuf->cur++, bo, - data, 0, flags, vor, tor); + return nouveau_pushbuf_emit_reloc(chan, chan->pushbuf->cur++, bo, + data, 0, flags, vor, tor); } -static __inline__ void +static __inline__ int OUT_RELOC2(struct nouveau_channel *chan, struct nouveau_bo *bo, unsigned data, unsigned data2, unsigned flags, unsigned vor, unsigned tor) { - nouveau_pushbuf_emit_reloc(chan, chan->pushbuf->cur++, bo, - data, data2, flags, vor, tor); + return nouveau_pushbuf_emit_reloc(chan, chan->pushbuf->cur++, bo, + data, data2, flags, vor, tor); } /* Raw data + flags depending on FB/TT buffer */ -static __inline__ void +static __inline__ int OUT_RELOCd(struct nouveau_channel *chan, struct nouveau_bo *bo, unsigned data, unsigned flags, unsigned vor, unsigned tor) { - OUT_RELOC(chan, bo, data, flags | NOUVEAU_BO_OR, vor, tor); + return OUT_RELOC(chan, bo, data, flags | NOUVEAU_BO_OR, vor, tor); } /* FB/TT object handle */ -static __inline__ void +static __inline__ int OUT_RELOCo(struct nouveau_channel *chan, struct nouveau_bo *bo, unsigned flags) { - OUT_RELOC(chan, bo, 0, flags | NOUVEAU_BO_OR, - chan->vram->handle, chan->gart->handle); + return OUT_RELOC(chan, bo, 0, flags | NOUVEAU_BO_OR, + chan->vram->handle, chan->gart->handle); } /* Low 32-bits of offset */ -static __inline__ void +static __inline__ int OUT_RELOCl(struct nouveau_channel *chan, struct nouveau_bo *bo, unsigned delta, unsigned flags) { - OUT_RELOC(chan, bo, delta, flags | NOUVEAU_BO_LOW, 0, 0); + return OUT_RELOC(chan, bo, delta, flags | NOUVEAU_BO_LOW, 0, 0); } /* Low 32-bits of offset + GPU linear access range info */ -static __inline__ void +static __inline__ int OUT_RELOCr(struct nouveau_channel *chan, struct nouveau_bo *bo, unsigned delta, unsigned size, unsigned flags) { - OUT_RELOC2(chan, bo, delta, size, flags | NOUVEAU_BO_LOW, 0, 0); + return OUT_RELOC2(chan, bo, delta, size, flags | NOUVEAU_BO_LOW, 0, 0); } /* High 32-bits of offset */ -static __inline__ void +static __inline__ int OUT_RELOCh(struct nouveau_channel *chan, struct nouveau_bo *bo, unsigned delta, unsigned flags) { - OUT_RELOC(chan, bo, delta, flags | NOUVEAU_BO_HIGH, 0, 0); + return OUT_RELOC(chan, bo, delta, flags | NOUVEAU_BO_HIGH, 0, 0); } #endif