1 | /* |
2 | * Copyright 2011 Red Hat, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * on the rights to use, copy, modify, merge, publish, distribute, sub |
8 | * license, and/or sell copies of the Software, and to permit persons to whom |
9 | * the Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 | */ |
22 | |
23 | #include <linux/iosys-map.h> |
24 | |
25 | #include <drm/drm_fourcc.h> |
26 | #include <drm/drm_framebuffer.h> |
27 | |
28 | #include "qxl_drv.h" |
29 | #include "qxl_object.h" |
30 | |
31 | static int alloc_clips(struct qxl_device *qdev, |
32 | struct qxl_release *release, |
33 | unsigned int num_clips, |
34 | struct qxl_bo **clips_bo) |
35 | { |
36 | int size = sizeof(struct qxl_clip_rects) + sizeof(struct qxl_rect) * num_clips; |
37 | |
38 | return qxl_alloc_bo_reserved(qdev, release, size, bo: clips_bo); |
39 | } |
40 | |
41 | /* returns a pointer to the already allocated qxl_rect array inside |
42 | * the qxl_clip_rects. This is *not* the same as the memory allocated |
43 | * on the device, it is offset to qxl_clip_rects.chunk.data */ |
44 | static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev, |
45 | unsigned int num_clips, |
46 | struct qxl_bo *clips_bo) |
47 | { |
48 | struct iosys_map map; |
49 | struct qxl_clip_rects *dev_clips; |
50 | int ret; |
51 | |
52 | ret = qxl_bo_vmap_locked(bo: clips_bo, map: &map); |
53 | if (ret) |
54 | return NULL; |
55 | dev_clips = map.vaddr; /* TODO: Use mapping abstraction properly */ |
56 | |
57 | dev_clips->num_rects = num_clips; |
58 | dev_clips->chunk.next_chunk = 0; |
59 | dev_clips->chunk.prev_chunk = 0; |
60 | dev_clips->chunk.data_size = sizeof(struct qxl_rect) * num_clips; |
61 | return (struct qxl_rect *)dev_clips->chunk.data; |
62 | } |
63 | |
64 | static int |
65 | alloc_drawable(struct qxl_device *qdev, struct qxl_release **release) |
66 | { |
67 | return qxl_alloc_release_reserved(qdev, size: sizeof(struct qxl_drawable), |
68 | type: QXL_RELEASE_DRAWABLE, release, NULL); |
69 | } |
70 | |
71 | static void |
72 | free_drawable(struct qxl_device *qdev, struct qxl_release *release) |
73 | { |
74 | qxl_release_free(qdev, release); |
75 | } |
76 | |
77 | /* release needs to be reserved at this point */ |
78 | static int |
79 | make_drawable(struct qxl_device *qdev, int surface, uint8_t type, |
80 | const struct qxl_rect *rect, |
81 | struct qxl_release *release) |
82 | { |
83 | struct qxl_drawable *drawable; |
84 | int i; |
85 | |
86 | drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); |
87 | if (!drawable) |
88 | return -ENOMEM; |
89 | |
90 | drawable->type = type; |
91 | |
92 | drawable->surface_id = surface; /* Only primary for now */ |
93 | drawable->effect = QXL_EFFECT_OPAQUE; |
94 | drawable->self_bitmap = 0; |
95 | drawable->self_bitmap_area.top = 0; |
96 | drawable->self_bitmap_area.left = 0; |
97 | drawable->self_bitmap_area.bottom = 0; |
98 | drawable->self_bitmap_area.right = 0; |
99 | /* FIXME: add clipping */ |
100 | drawable->clip.type = SPICE_CLIP_TYPE_NONE; |
101 | |
102 | /* |
103 | * surfaces_dest[i] should apparently be filled out with the |
104 | * surfaces that we depend on, and surface_rects should be |
105 | * filled with the rectangles of those surfaces that we |
106 | * are going to use. |
107 | */ |
108 | for (i = 0; i < 3; ++i) |
109 | drawable->surfaces_dest[i] = -1; |
110 | |
111 | if (rect) |
112 | drawable->bbox = *rect; |
113 | |
114 | drawable->mm_time = qdev->rom->mm_clock; |
115 | qxl_release_unmap(qdev, release, info: &drawable->release_info); |
116 | return 0; |
117 | } |
118 | |
119 | /* push a draw command using the given clipping rectangles as |
120 | * the sources from the shadow framebuffer. |
121 | * |
122 | * Right now implementing with a single draw and a clip list. Clip |
123 | * lists are known to be a problem performance wise, this can be solved |
124 | * by treating them differently in the server. |
125 | */ |
126 | void qxl_draw_dirty_fb(struct qxl_device *qdev, |
127 | struct drm_framebuffer *fb, |
128 | struct qxl_bo *bo, |
129 | unsigned int flags, unsigned int color, |
130 | struct drm_clip_rect *clips, |
131 | unsigned int num_clips, int inc, |
132 | uint32_t dumb_shadow_offset) |
133 | { |
134 | /* |
135 | * TODO: if flags & DRM_MODE_FB_DIRTY_ANNOTATE_FILL then we should |
136 | * send a fill command instead, much cheaper. |
137 | * |
138 | * See include/drm/drm_mode.h |
139 | */ |
140 | struct drm_clip_rect *clips_ptr; |
141 | int i; |
142 | int left, right, top, bottom; |
143 | int width, height; |
144 | struct qxl_drawable *drawable; |
145 | struct qxl_rect drawable_rect; |
146 | struct qxl_rect *rects; |
147 | int stride = fb->pitches[0]; |
148 | /* depth is not actually interesting, we don't mask with it */ |
149 | int depth = fb->format->cpp[0] * 8; |
150 | struct iosys_map surface_map; |
151 | uint8_t *surface_base; |
152 | struct qxl_release *release; |
153 | struct qxl_bo *clips_bo; |
154 | struct qxl_drm_image *dimage; |
155 | int ret; |
156 | |
157 | ret = alloc_drawable(qdev, release: &release); |
158 | if (ret) |
159 | return; |
160 | |
161 | clips->x1 += dumb_shadow_offset; |
162 | clips->x2 += dumb_shadow_offset; |
163 | |
164 | left = clips->x1; |
165 | right = clips->x2; |
166 | top = clips->y1; |
167 | bottom = clips->y2; |
168 | |
169 | /* skip the first clip rect */ |
170 | for (i = 1, clips_ptr = clips + inc; |
171 | i < num_clips; i++, clips_ptr += inc) { |
172 | left = min_t(int, left, (int)clips_ptr->x1); |
173 | right = max_t(int, right, (int)clips_ptr->x2); |
174 | top = min_t(int, top, (int)clips_ptr->y1); |
175 | bottom = max_t(int, bottom, (int)clips_ptr->y2); |
176 | } |
177 | |
178 | width = right - left; |
179 | height = bottom - top; |
180 | |
181 | ret = alloc_clips(qdev, release, num_clips, clips_bo: &clips_bo); |
182 | if (ret) |
183 | goto out_free_drawable; |
184 | |
185 | ret = qxl_image_alloc_objects(qdev, release, |
186 | image_ptr: &dimage, |
187 | height, stride); |
188 | if (ret) |
189 | goto out_free_clips; |
190 | |
191 | /* do a reservation run over all the objects we just allocated */ |
192 | ret = qxl_release_reserve_list(release, no_intr: true); |
193 | if (ret) |
194 | goto out_free_image; |
195 | |
196 | drawable_rect.left = left; |
197 | drawable_rect.right = right; |
198 | drawable_rect.top = top; |
199 | drawable_rect.bottom = bottom; |
200 | |
201 | ret = make_drawable(qdev, surface: 0, type: QXL_DRAW_COPY, rect: &drawable_rect, |
202 | release); |
203 | if (ret) |
204 | goto out_release_backoff; |
205 | |
206 | ret = qxl_bo_vmap_locked(bo, map: &surface_map); |
207 | if (ret) |
208 | goto out_release_backoff; |
209 | surface_base = surface_map.vaddr; /* TODO: Use mapping abstraction properly */ |
210 | |
211 | ret = qxl_image_init(qdev, release, dimage, data: surface_base, |
212 | x: left - dumb_shadow_offset, |
213 | y: top, width, height, depth, stride); |
214 | qxl_bo_vunmap_locked(bo); |
215 | if (ret) |
216 | goto out_release_backoff; |
217 | |
218 | rects = drawable_set_clipping(qdev, num_clips, clips_bo); |
219 | if (!rects) { |
220 | ret = -EINVAL; |
221 | goto out_release_backoff; |
222 | } |
223 | drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); |
224 | |
225 | drawable->clip.type = SPICE_CLIP_TYPE_RECTS; |
226 | drawable->clip.data = qxl_bo_physical_address(qdev, |
227 | bo: clips_bo, offset: 0); |
228 | |
229 | drawable->u.copy.src_area.top = 0; |
230 | drawable->u.copy.src_area.bottom = height; |
231 | drawable->u.copy.src_area.left = 0; |
232 | drawable->u.copy.src_area.right = width; |
233 | |
234 | drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; |
235 | drawable->u.copy.scale_mode = 0; |
236 | drawable->u.copy.mask.flags = 0; |
237 | drawable->u.copy.mask.pos.x = 0; |
238 | drawable->u.copy.mask.pos.y = 0; |
239 | drawable->u.copy.mask.bitmap = 0; |
240 | |
241 | drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, bo: dimage->bo, offset: 0); |
242 | qxl_release_unmap(qdev, release, info: &drawable->release_info); |
243 | |
244 | clips_ptr = clips; |
245 | for (i = 0; i < num_clips; i++, clips_ptr += inc) { |
246 | rects[i].left = clips_ptr->x1; |
247 | rects[i].right = clips_ptr->x2; |
248 | rects[i].top = clips_ptr->y1; |
249 | rects[i].bottom = clips_ptr->y2; |
250 | } |
251 | qxl_bo_vunmap_locked(bo: clips_bo); |
252 | |
253 | qxl_release_fence_buffer_objects(release); |
254 | qxl_push_command_ring_release(qdev, release, type: QXL_CMD_DRAW, interruptible: false); |
255 | |
256 | out_release_backoff: |
257 | if (ret) |
258 | qxl_release_backoff_reserve_list(release); |
259 | out_free_image: |
260 | qxl_image_free_objects(qdev, dimage); |
261 | out_free_clips: |
262 | qxl_bo_unref(bo: &clips_bo); |
263 | out_free_drawable: |
264 | /* only free drawable on error */ |
265 | if (ret) |
266 | free_drawable(qdev, release); |
267 | |
268 | } |
269 | |