On Thursday 19 February 2009 16:43:54 Jesse Barnes wrote: > On Thursday 19 February 2009 11:37:01 Chris Wilson wrote: > > With a few additional suggestions by Jesse, I've managed to get > > tear-free compositing working on i915. Here's the diff on top of the > > original patch (though obviously this is just a suggestion, still need > > to prevent multiple pending flips to the same plane and ensure that the > > old buffer is eventually unpinned, and you might choose to drop the > > mutex around the wait_for_vblank ;-): > > Yeah, looks pretty reasonable. Here's what I've been working with. It > adds a couple of more changes (and slightly different cleanups) over your > version: - added a seqno to wait for > - wait for flip before queuing another > but it's missing thew 915 changes you added (btw did I get the pitch wrong? > or are you submitting a different value for your objects?). > > I added a new seqno since it's possible (even likely) that the next vblank > won't actually reflect the flip, if the GPU is busy processing a large > batchbuffer when the ring command goes in for example. > > Also it might be a good idea to wait for any previous flip before queuing a > new one. Anyway still tracking down some issues with the X side, but it > seems like it's approaching readiness.
Oh you want to actually see the code? diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index cbb8224..4e4fb61 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -829,6 +829,152 @@ static int i915_set_status_page(struct drm_device *dev, void *data, return 0; } +static int i915_pipe_to_plane(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 reg, pipe_mask; + + if (pipe == 0) + pipe_mask = DISPPLANE_SEL_PIPE_A; + else + pipe_mask = DISPPLANE_SEL_PIPE_B; + + pipe_mask |= DISPLAY_PLANE_ENABLE; + + reg = I915_READ(DSPACNTR); + if ((reg & (DISPLAY_PLANE_ENABLE | DISPPLANE_SEL_PIPE_MASK)) == + pipe_mask) + return 0; + + reg = I915_READ(DSPBCNTR); + if ((reg & (DISPLAY_PLANE_ENABLE | DISPPLANE_SEL_PIPE_MASK)) == + pipe_mask) + return 1; + + return -1; +} + +bool +i915_gem_flip_pending(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + unsigned long flags; + bool pending = false; + + spin_lock_irqsave(&dev_priv->vblank_lock, flags); + if (!list_empty(&obj_priv->vblank_head)) + pending = true; + spin_unlock_irqrestore(&dev_priv->vblank_lock, flags); + + return pending; +} + +static int i915_gem_page_flip(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_i915_gem_page_flip *flip_data = data; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + unsigned long flags; + uint32_t offset; + int pitch, pipe, plane, tiled; + int ret = 0; + RING_LOCALS; + + if (!(dev->driver->driver_features & DRIVER_GEM)) + return -ENODEV; + + /* + * Reject unknown flags so future userspace knows what we (don't) + * support + */ + if (flip_data->flags & (~I915_PAGE_FLIP_WAIT)) { + DRM_ERROR("bad page flip flags\n"); + return -EINVAL; + } + + if ((pipe = flip_data->pipe) > 1) { + DRM_ERROR("bad pipe\n"); + return -EINVAL; + } + + plane = i915_pipe_to_plane(dev, pipe); + if (plane < 0) { + DRM_ERROR("bad pipe (no planes enabled?)\n"); + return -EINVAL; + } + + obj = drm_gem_object_lookup(dev, file_priv, flip_data->handle); + if (obj == NULL) + return -EBADF; + + if (i915_gem_flip_pending(obj)) + wait_for_completion(&obj_priv->vblank); + + mutex_lock(&dev->struct_mutex); + + obj_priv = obj->driver_private; + if (obj_priv->tiling_mode != I915_TILING_NONE && + obj_priv->tiling_mode != I915_TILING_X) { + DRM_ERROR("can only flip non-tiled or X tiled pages\n"); + ret = -EINVAL; + goto out_unref; + } + + ret = i915_gem_object_pin(obj, 0); + if (ret) { + DRM_ERROR("failed to pin object for flip\n"); + ret = -EBUSY; + goto out_unref; + } + mutex_unlock(&dev->struct_mutex); + + /* + * Put the object in the GTT domain before the flip, + * since there may be outstanding rendering + */ + i915_gem_object_set_to_gtt_domain(obj, 0); + + offset = obj_priv->gtt_offset; + pitch = obj_priv->stride; + tiled = !!(obj_priv->tiling_mode == I915_TILING_X); + + /* + * Queue the flip with the lock held in case the flip happens + * immediately. + */ + spin_lock_irqsave(&dev_priv->vblank_lock, flags); + + BEGIN_LP_RING(4); + OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (plane << 20)); + OUT_RING((pitch / 8) << 3); /* flip queue and/or pitch */ + OUT_RING(offset | tiled); + OUT_RING(((flip_data->x - 1) << 16) | (flip_data->y - 1)); + ADVANCE_LP_RING(); + + list_add_tail(&obj_priv->vblank_head, &dev_priv->mm.vblank_list[pipe]); + + obj_priv->flip_seqno = i915_add_request(dev, 0); + + drm_vblank_get(dev, pipe); + + spin_unlock_irqrestore(&dev_priv->vblank_lock, flags); + + if (flip_data->flags & I915_PAGE_FLIP_WAIT) + wait_for_completion(&obj_priv->vblank); + + /* Take lock again for unref */ + mutex_lock(&dev->struct_mutex); +out_unref: + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + /** * i915_probe_agp - get AGP bootup configuration * @pdev: PCI device @@ -1300,6 +1446,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0), DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0), DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0), + DRM_IOCTL_DEF(DRM_I915_GEM_PAGE_FLIP, i915_gem_page_flip, DRM_AUTH|DRM_MASTER), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7ed981c..c75735d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -161,6 +161,9 @@ typedef struct drm_i915_private { u32 irq_mask_reg; u32 pipestat[2]; + /** Protects vblank list */ + spinlock_t vblank_lock; + u32 hotplug_supported_mask; struct work_struct hotplug_work; @@ -343,6 +346,11 @@ typedef struct drm_i915_private { */ struct delayed_work retire_work; + /** + * List of objects waiting on vblank events (one per pipe) + */ + struct list_head vblank_list[2]; + uint32_t next_gem_seqno; /** @@ -394,6 +402,11 @@ struct drm_i915_gem_object { /** This object's place on the active/flushing/inactive lists */ struct list_head list; + /** Object's place on the vblank list */ + struct list_head vblank_head; + /** sequence number for flip (when it passes the flip is done) */ + int flip_seqno; + /** * This is set if the object is on the active or flushing lists * (has pending rendering), and is not set if it's on inactive (ready @@ -462,6 +475,9 @@ struct drm_i915_gem_object { /** for phy allocated objects */ struct drm_i915_gem_phys_object *phys_obj; + + /** for page flips and other vblank related blocks */ + struct completion vblank; }; /** @@ -521,6 +537,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, extern int i915_emit_box(struct drm_device *dev, struct drm_clip_rect __user *boxes, int i, int DR1, int DR4); +extern bool i915_gem_flip_pending(struct drm_gem_object *obj); /* i915_irq.c */ extern int i915_irq_emit(struct drm_device *dev, void *data, @@ -629,6 +646,9 @@ int i915_gem_attach_phys_object(struct drm_device *dev, void i915_gem_detach_phys_object(struct drm_device *dev, struct drm_gem_object *obj); void i915_gem_free_all_phys_object(struct drm_device *dev); +uint32_t i915_add_request(struct drm_device *dev, uint32_t flush_domains); +int i915_seqno_passed(uint32_t seq1, uint32_t seq2); +uint32_t i915_get_gem_seqno(struct drm_device *dev); /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index da9a2cb..a2f2a9d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -917,7 +917,7 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj) * * Returned sequence numbers are nonzero on success. */ -static uint32_t +uint32_t i915_add_request(struct drm_device *dev, uint32_t flush_domains) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -1045,7 +1045,7 @@ i915_gem_retire_request(struct drm_device *dev, /** * Returns true if seq1 is later than seq2. */ -static int +int i915_seqno_passed(uint32_t seq1, uint32_t seq2) { return (int32_t)(seq1 - seq2) >= 0; @@ -2547,6 +2547,11 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, for (pin_tries = 0; ; pin_tries++) { ret = 0; for (i = 0; i < args->buffer_count; i++) { + struct drm_i915_gem_object *obj_priv; + + obj_priv = object_list[i]->driver_private; + if (i915_gem_flip_pending(object_list[i])) + wait_for_completion(&obj_priv->vblank); object_list[i]->pending_read_domains = 0; object_list[i]->pending_write_domain = 0; ret = i915_gem_object_pin_and_relocate(object_list[i], @@ -2924,6 +2929,9 @@ int i915_gem_init_object(struct drm_gem_object *obj) obj_priv->obj = obj; obj_priv->fence_reg = I915_FENCE_REG_NONE; INIT_LIST_HEAD(&obj_priv->list); + INIT_LIST_HEAD(&obj_priv->vblank_head); + + init_completion(&obj_priv->vblank); return 0; } @@ -3358,9 +3366,12 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->mm.flushing_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); INIT_LIST_HEAD(&dev_priv->mm.request_list); + INIT_LIST_HEAD(&dev_priv->mm.vblank_list[0]); + INIT_LIST_HEAD(&dev_priv->mm.vblank_list[1]); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); dev_priv->mm.next_gem_seqno = 1; + spin_lock_init(&dev_priv->vblank_lock); /* Old X drivers will take 0-2 for front, back, depth buffers */ dev_priv->fence_reg_start = 3; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 019140e..66cd33d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -196,6 +196,25 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_sysfs_hotplug_event(dev); } +static void i915_complete_vblank(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv, *tmp; + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->vblank_lock, irqflags); + list_for_each_entry_safe(obj_priv, tmp, &dev_priv->mm.vblank_list[pipe], + vblank_head) { + int cur_seqno = i915_get_gem_seqno(dev); + if (i915_seqno_passed(cur_seqno, obj_priv->flip_seqno)) { + complete(&obj_priv->vblank); + list_del_init(&obj_priv->vblank_head); + drm_vblank_put(dev, pipe); + } + } + spin_unlock_irqrestore(&dev_priv->vblank_lock, irqflags); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -284,11 +303,13 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) if (pipea_stats & vblank_status) { vblank++; + i915_complete_vblank(dev, 0); drm_handle_vblank(dev, 0); } if (pipeb_stats & vblank_status) { vblank++; + i915_complete_vblank(dev, 1); drm_handle_vblank(dev, 1); } diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 912cd52..0678e47 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -184,6 +184,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GEM_GET_TILING 0x22 #define DRM_I915_GEM_GET_APERTURE 0x23 #define DRM_I915_GEM_MMAP_GTT 0x24 +#define DRM_I915_GEM_PAGE_FLIP 0x25 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -219,6 +220,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) #define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling) #define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) +#define DRM_IOCTL_I915_GEM_PAGE_FLIP DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PAGE_FLIP, struct drm_i915_gem_page_flip) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -654,4 +656,27 @@ struct drm_i915_gem_get_aperture { uint64_t aper_available_size; }; +#define I915_PAGE_FLIP_WAIT (1<<0) /* block on page flip completion */ + +struct drm_i915_gem_page_flip { + /** Handle of new front buffer */ + uint32_t handle; + + /** + * page flip flags (wait on flip only for now) + */ + uint32_t flags; + + /** + * pipe to flip + */ + uint32_t pipe; + + /** + * screen dimensions for flip + */ + uint32_t x; + uint32_t y; +}; + #endif /* _I915_DRM_H_ */ ------------------------------------------------------------------------------ Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA -OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise -Strategies to boost innovation and cut costs with open source participation -Receive a $600 discount off the registration fee with the source code: SFAD http://p.sf.net/sfu/XcvMzF8H -- _______________________________________________ Dri-devel mailing list Dri-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dri-devel