diff options
author | =?utf-8?q?Michel_D=C3=A4nzer?= <michel@tungstengraphics.com> | 2006-12-11 02:32:27 -0500 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2006-12-11 02:32:27 -0500 |
commit | 3188a24c256bae0ed93d81d82db1f1bb6060d727 (patch) | |
tree | 8161deb13ff3c135eb274b08295920fb00ad8bf0 /drivers/char/drm | |
parent | 2c3f0eddfbd7f5c7a5450de287bad805722888c3 (diff) |
i915_vblank_tasklet: Try harder to avoid tearing.
Previously, if there were several buffer swaps scheduled for the same vertical
blank, all but the first blit emitted stood a chance of exhibiting tearing. In
order to avoid this, split the blits along slices of each output top to bottom.
Signed-off-by: Dave Airlie <airlied@linux.ie>
Diffstat (limited to 'drivers/char/drm')
-rw-r--r-- | drivers/char/drm/i915_irq.c | 199 |
1 files changed, 139 insertions, 60 deletions
diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index e5463b111fc0..e2c4b3a41b1e 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c | |||
@@ -46,88 +46,167 @@ static void i915_vblank_tasklet(drm_device_t *dev) | |||
46 | { | 46 | { |
47 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 47 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
48 | unsigned long irqflags; | 48 | unsigned long irqflags; |
49 | struct list_head *list, *tmp; | 49 | struct list_head *list, *tmp, hits, *hit; |
50 | int nhits, nrects, slice[2], upper[2], lower[2], i; | ||
51 | unsigned counter[2] = { atomic_read(&dev->vbl_received), | ||
52 | atomic_read(&dev->vbl_received2) }; | ||
53 | drm_drawable_info_t *drw; | ||
54 | drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; | ||
55 | u32 cpp = dev_priv->cpp; | ||
56 | u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | | ||
57 | XY_SRC_COPY_BLT_WRITE_ALPHA | | ||
58 | XY_SRC_COPY_BLT_WRITE_RGB) | ||
59 | : XY_SRC_COPY_BLT_CMD; | ||
60 | u32 pitchropcpp = (sarea_priv->pitch * cpp) | (0xcc << 16) | | ||
61 | (cpp << 23) | (1 << 24); | ||
62 | RING_LOCALS; | ||
50 | 63 | ||
51 | DRM_DEBUG("\n"); | 64 | DRM_DEBUG("\n"); |
52 | 65 | ||
66 | INIT_LIST_HEAD(&hits); | ||
67 | |||
68 | nhits = nrects = 0; | ||
69 | |||
53 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | 70 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); |
54 | 71 | ||
72 | /* Find buffer swaps scheduled for this vertical blank */ | ||
55 | list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { | 73 | list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { |
56 | drm_i915_vbl_swap_t *vbl_swap = | 74 | drm_i915_vbl_swap_t *vbl_swap = |
57 | list_entry(list, drm_i915_vbl_swap_t, head); | 75 | list_entry(list, drm_i915_vbl_swap_t, head); |
58 | atomic_t *counter = vbl_swap->pipe ? &dev->vbl_received2 : | ||
59 | &dev->vbl_received; | ||
60 | |||
61 | if ((atomic_read(counter) - vbl_swap->sequence) <= (1<<23)) { | ||
62 | drm_drawable_info_t *drw; | ||
63 | |||
64 | spin_unlock(&dev_priv->swaps_lock); | ||
65 | |||
66 | spin_lock(&dev->drw_lock); | ||
67 | |||
68 | drw = drm_get_drawable_info(dev, vbl_swap->drw_id); | ||
69 | |||
70 | if (drw) { | ||
71 | int i, num_rects = drw->num_rects; | ||
72 | drm_clip_rect_t *rect = drw->rects; | ||
73 | drm_i915_sarea_t *sarea_priv = | ||
74 | dev_priv->sarea_priv; | ||
75 | u32 cpp = dev_priv->cpp; | ||
76 | u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | | ||
77 | XY_SRC_COPY_BLT_WRITE_ALPHA | | ||
78 | XY_SRC_COPY_BLT_WRITE_RGB) | ||
79 | : XY_SRC_COPY_BLT_CMD; | ||
80 | u32 pitchropcpp = (sarea_priv->pitch * cpp) | | ||
81 | (0xcc << 16) | (cpp << 23) | | ||
82 | (1 << 24); | ||
83 | RING_LOCALS; | ||
84 | |||
85 | i915_kernel_lost_context(dev); | ||
86 | |||
87 | BEGIN_LP_RING(6); | ||
88 | |||
89 | OUT_RING(GFX_OP_DRAWRECT_INFO); | ||
90 | OUT_RING(0); | ||
91 | OUT_RING(0); | ||
92 | OUT_RING(sarea_priv->width | | ||
93 | sarea_priv->height << 16); | ||
94 | OUT_RING(sarea_priv->width | | ||
95 | sarea_priv->height << 16); | ||
96 | OUT_RING(0); | ||
97 | 76 | ||
98 | ADVANCE_LP_RING(); | 77 | if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23)) |
78 | continue; | ||
79 | |||
80 | list_del(list); | ||
81 | dev_priv->swaps_pending--; | ||
99 | 82 | ||
100 | sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; | 83 | spin_unlock(&dev_priv->swaps_lock); |
84 | spin_lock(&dev->drw_lock); | ||
101 | 85 | ||
102 | for (i = 0; i < num_rects; i++, rect++) { | 86 | drw = drm_get_drawable_info(dev, vbl_swap->drw_id); |
103 | BEGIN_LP_RING(8); | 87 | |
88 | if (!drw) { | ||
89 | spin_unlock(&dev->drw_lock); | ||
90 | drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); | ||
91 | spin_lock(&dev_priv->swaps_lock); | ||
92 | continue; | ||
93 | } | ||
104 | 94 | ||
105 | OUT_RING(cmd); | 95 | list_for_each(hit, &hits) { |
106 | OUT_RING(pitchropcpp); | 96 | drm_i915_vbl_swap_t *swap_cmp = |
107 | OUT_RING((rect->y1 << 16) | rect->x1); | 97 | list_entry(hit, drm_i915_vbl_swap_t, head); |
108 | OUT_RING((rect->y2 << 16) | rect->x2); | 98 | drm_drawable_info_t *drw_cmp = |
109 | OUT_RING(sarea_priv->front_offset); | 99 | drm_get_drawable_info(dev, swap_cmp->drw_id); |
110 | OUT_RING((rect->y1 << 16) | rect->x1); | ||
111 | OUT_RING(pitchropcpp & 0xffff); | ||
112 | OUT_RING(sarea_priv->back_offset); | ||
113 | 100 | ||
114 | ADVANCE_LP_RING(); | 101 | if (drw_cmp && |
115 | } | 102 | drw_cmp->rects[0].y1 > drw->rects[0].y1) { |
103 | list_add_tail(list, hit); | ||
104 | break; | ||
116 | } | 105 | } |
106 | } | ||
117 | 107 | ||
118 | spin_unlock(&dev->drw_lock); | 108 | spin_unlock(&dev->drw_lock); |
119 | 109 | ||
120 | spin_lock(&dev_priv->swaps_lock); | 110 | /* List of hits was empty, or we reached the end of it */ |
111 | if (hit == &hits) | ||
112 | list_add_tail(list, hits.prev); | ||
121 | 113 | ||
122 | list_del(list); | 114 | nhits++; |
123 | 115 | ||
124 | drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); | 116 | spin_lock(&dev_priv->swaps_lock); |
117 | } | ||
118 | |||
119 | if (nhits == 0) { | ||
120 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
121 | return; | ||
122 | } | ||
123 | |||
124 | spin_unlock(&dev_priv->swaps_lock); | ||
125 | 125 | ||
126 | dev_priv->swaps_pending--; | 126 | i915_kernel_lost_context(dev); |
127 | |||
128 | BEGIN_LP_RING(6); | ||
129 | |||
130 | OUT_RING(GFX_OP_DRAWRECT_INFO); | ||
131 | OUT_RING(0); | ||
132 | OUT_RING(0); | ||
133 | OUT_RING(sarea_priv->width | sarea_priv->height << 16); | ||
134 | OUT_RING(sarea_priv->width | sarea_priv->height << 16); | ||
135 | OUT_RING(0); | ||
136 | |||
137 | ADVANCE_LP_RING(); | ||
138 | |||
139 | sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; | ||
140 | |||
141 | upper[0] = upper[1] = 0; | ||
142 | slice[0] = max(sarea_priv->pipeA_h / nhits, 1); | ||
143 | slice[1] = max(sarea_priv->pipeB_h / nhits, 1); | ||
144 | lower[0] = sarea_priv->pipeA_y + slice[0]; | ||
145 | lower[1] = sarea_priv->pipeB_y + slice[0]; | ||
146 | |||
147 | spin_lock(&dev->drw_lock); | ||
148 | |||
149 | /* Emit blits for buffer swaps, partitioning both outputs into as many | ||
150 | * slices as there are buffer swaps scheduled in order to avoid tearing | ||
151 | * (based on the assumption that a single buffer swap would always | ||
152 | * complete before scanout starts). | ||
153 | */ | ||
154 | for (i = 0; i++ < nhits; | ||
155 | upper[0] = lower[0], lower[0] += slice[0], | ||
156 | upper[1] = lower[1], lower[1] += slice[1]) { | ||
157 | if (i == nhits) | ||
158 | lower[0] = lower[1] = sarea_priv->height; | ||
159 | |||
160 | list_for_each(hit, &hits) { | ||
161 | drm_i915_vbl_swap_t *swap_hit = | ||
162 | list_entry(hit, drm_i915_vbl_swap_t, head); | ||
163 | drm_clip_rect_t *rect; | ||
164 | int num_rects, pipe; | ||
165 | unsigned short top, bottom; | ||
166 | |||
167 | drw = drm_get_drawable_info(dev, swap_hit->drw_id); | ||
168 | |||
169 | if (!drw) | ||
170 | continue; | ||
171 | |||
172 | rect = drw->rects; | ||
173 | pipe = swap_hit->pipe; | ||
174 | top = upper[pipe]; | ||
175 | bottom = lower[pipe]; | ||
176 | |||
177 | for (num_rects = drw->num_rects; num_rects--; rect++) { | ||
178 | int y1 = max(rect->y1, top); | ||
179 | int y2 = min(rect->y2, bottom); | ||
180 | |||
181 | if (y1 >= y2) | ||
182 | continue; | ||
183 | |||
184 | BEGIN_LP_RING(8); | ||
185 | |||
186 | OUT_RING(cmd); | ||
187 | OUT_RING(pitchropcpp); | ||
188 | OUT_RING((y1 << 16) | rect->x1); | ||
189 | OUT_RING((y2 << 16) | rect->x2); | ||
190 | OUT_RING(sarea_priv->front_offset); | ||
191 | OUT_RING((y1 << 16) | rect->x1); | ||
192 | OUT_RING(pitchropcpp & 0xffff); | ||
193 | OUT_RING(sarea_priv->back_offset); | ||
194 | |||
195 | ADVANCE_LP_RING(); | ||
196 | } | ||
127 | } | 197 | } |
128 | } | 198 | } |
129 | 199 | ||
130 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | 200 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); |
201 | |||
202 | list_for_each_safe(hit, tmp, &hits) { | ||
203 | drm_i915_vbl_swap_t *swap_hit = | ||
204 | list_entry(hit, drm_i915_vbl_swap_t, head); | ||
205 | |||
206 | list_del(hit); | ||
207 | |||
208 | drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); | ||
209 | } | ||
131 | } | 210 | } |
132 | 211 | ||
133 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | 212 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) |