1 /* Cairo - a vector graphics library with display and print output
2 *
3 * Copyright © 2009 Chris Wilson
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
12 *
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
18 *
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.org/MPL/
23 *
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
27 *
28 */
29
30 #include "cairoint.h"
31
32 #include "cairo-drm-private.h"
33 #include "cairo-drm-radeon-private.h"
34
35 #include "cairo-default-context-private.h"
36 #include "cairo-error-private.h"
37 #include "cairo-image-surface-private.h"
38
39 #include <stddef.h>
40 #include <inttypes.h> /* workaround for broken <drm/radeon_drm.h> */
41 #include <drm/radeon_drm.h>
42
43 /* Basic stub surface for radeon chipsets */
44
45 #define MAX_SIZE 2048
46
47 typedef struct _radeon_surface {
48 cairo_drm_surface_t base;
49 } radeon_surface_t;
50
51 static inline radeon_device_t *
to_radeon_device(cairo_device_t * device)52 to_radeon_device (cairo_device_t *device)
53 {
54 return (radeon_device_t *) device;
55 }
56
57 static inline radeon_bo_t *
to_radeon_bo(cairo_drm_bo_t * bo)58 to_radeon_bo (cairo_drm_bo_t *bo)
59 {
60 return (radeon_bo_t *) bo;
61 }
62
63 static cairo_surface_t *
radeon_surface_create_similar(void * abstract_surface,cairo_content_t content,int width,int height)64 radeon_surface_create_similar (void *abstract_surface,
65 cairo_content_t content,
66 int width,
67 int height)
68 {
69 return cairo_image_surface_create (_cairo_format_from_content (content),
70 width, height);
71 }
72
73 static cairo_status_t
radeon_surface_finish(void * abstract_surface)74 radeon_surface_finish (void *abstract_surface)
75 {
76 radeon_surface_t *surface = abstract_surface;
77
78 return _cairo_drm_surface_finish (&surface->base);
79 }
80
81 static cairo_status_t
radeon_surface_acquire_source_image(void * abstract_surface,cairo_image_surface_t ** image_out,void ** image_extra)82 radeon_surface_acquire_source_image (void *abstract_surface,
83 cairo_image_surface_t **image_out,
84 void **image_extra)
85 {
86 radeon_surface_t *surface = abstract_surface;
87 cairo_surface_t *image;
88 cairo_status_t status;
89
90 /* XXX batch flush */
91
92 if (surface->base.fallback != NULL) {
93 image = surface->base.fallback;
94 goto DONE;
95 }
96
97 image = _cairo_surface_has_snapshot (&surface->base.base,
98 &_cairo_image_surface_backend);
99 if (image != NULL)
100 goto DONE;
101
102 if (surface->base.base.backend->flush != NULL) {
103 status = surface->base.base.backend->flush (surface);
104 if (unlikely (status))
105 return status;
106 }
107
108 image = radeon_bo_get_image (to_radeon_device (surface->base.base.device),
109 to_radeon_bo (surface->base.bo),
110 &surface->base);
111 status = image->status;
112 if (unlikely (status))
113 return status;
114
115 _cairo_surface_attach_snapshot (&surface->base.base, image, cairo_surface_destroy);
116
117 DONE:
118 *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
119 *image_extra = NULL;
120 return CAIRO_STATUS_SUCCESS;
121 }
122
123 static void
radeon_surface_release_source_image(void * abstract_surface,cairo_image_surface_t * image,void * image_extra)124 radeon_surface_release_source_image (void *abstract_surface,
125 cairo_image_surface_t *image,
126 void *image_extra)
127 {
128 cairo_surface_destroy (&image->base);
129 }
130
131 static cairo_surface_t *
radeon_surface_map_to_image(radeon_surface_t * surface)132 radeon_surface_map_to_image (radeon_surface_t *surface)
133 {
134 if (surface->base.fallback == NULL) {
135 cairo_surface_t *image;
136 cairo_status_t status;
137 void *ptr;
138
139 if (surface->base.base.backend->flush != NULL) {
140 status = surface->base.base.backend->flush (surface);
141 if (unlikely (status))
142 return _cairo_surface_create_in_error (status);
143 }
144
145 ptr = radeon_bo_map (to_radeon_device (surface->base.base.device),
146 to_radeon_bo (surface->base.bo));
147 if (unlikely (ptr == NULL))
148 return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
149
150 image = cairo_image_surface_create_for_data (ptr,
151 surface->base.format,
152 surface->base.width,
153 surface->base.height,
154 surface->base.stride);
155 if (unlikely (image->status)) {
156 radeon_bo_unmap (to_radeon_bo (surface->base.bo));
157 return image;
158 }
159
160 surface->base.fallback = image;
161 }
162
163 return surface->base.fallback;
164 }
165
166 static cairo_status_t
radeon_surface_flush(void * abstract_surface,unsigned flags)167 radeon_surface_flush (void *abstract_surface,
168 unsigned flags)
169 {
170 radeon_surface_t *surface = abstract_surface;
171 cairo_status_t status;
172
173 if (flags)
174 return CAIRO_STATUS_SUCCESS;
175
176 if (surface->base.fallback == NULL)
177 return CAIRO_STATUS_SUCCESS;
178
179 /* kill any outstanding maps */
180 cairo_surface_finish (surface->base.fallback);
181
182 status = cairo_surface_status (surface->base.fallback);
183 cairo_surface_destroy (surface->base.fallback);
184 surface->base.fallback = NULL;
185
186 radeon_bo_unmap (to_radeon_bo (surface->base.bo));
187
188 return status;
189 }
190
191 static cairo_int_status_t
radeon_surface_paint(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_clip_t * clip)192 radeon_surface_paint (void *abstract_surface,
193 cairo_operator_t op,
194 const cairo_pattern_t *source,
195 cairo_clip_t *clip)
196 {
197 return _cairo_surface_paint (radeon_surface_map_to_image (abstract_surface),
198 op, source, clip);
199 }
200
201 static cairo_int_status_t
radeon_surface_mask(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,cairo_clip_t * clip)202 radeon_surface_mask (void *abstract_surface,
203 cairo_operator_t op,
204 const cairo_pattern_t *source,
205 const cairo_pattern_t *mask,
206 cairo_clip_t *clip)
207 {
208 return _cairo_surface_mask (radeon_surface_map_to_image (abstract_surface),
209 op, source, mask, clip);
210 }
211
212 static cairo_int_status_t
radeon_surface_stroke(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_path_fixed_t * path,const cairo_stroke_style_t * stroke_style,const cairo_matrix_t * ctm,const cairo_matrix_t * ctm_inverse,double tolerance,cairo_antialias_t antialias,cairo_clip_t * clip)213 radeon_surface_stroke (void *abstract_surface,
214 cairo_operator_t op,
215 const cairo_pattern_t *source,
216 cairo_path_fixed_t *path,
217 const cairo_stroke_style_t *stroke_style,
218 const cairo_matrix_t *ctm,
219 const cairo_matrix_t *ctm_inverse,
220 double tolerance,
221 cairo_antialias_t antialias,
222 cairo_clip_t *clip)
223 {
224 return _cairo_surface_stroke (radeon_surface_map_to_image (abstract_surface),
225 op, source, path, stroke_style, ctm, ctm_inverse,
226 tolerance, antialias, clip);
227 }
228
229 static cairo_int_status_t
radeon_surface_fill(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_path_fixed_t * path,cairo_fill_rule_t fill_rule,double tolerance,cairo_antialias_t antialias,cairo_clip_t * clip)230 radeon_surface_fill (void *abstract_surface,
231 cairo_operator_t op,
232 const cairo_pattern_t *source,
233 cairo_path_fixed_t *path,
234 cairo_fill_rule_t fill_rule,
235 double tolerance,
236 cairo_antialias_t antialias,
237 cairo_clip_t *clip)
238 {
239 return _cairo_surface_fill (radeon_surface_map_to_image (abstract_surface),
240 op, source, path, fill_rule,
241 tolerance, antialias, clip);
242 }
243
244 static cairo_int_status_t
radeon_surface_glyphs(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_glyph_t * glyphs,int num_glyphs,cairo_scaled_font_t * scaled_font,cairo_clip_t * clip,int * num_remaining)245 radeon_surface_glyphs (void *abstract_surface,
246 cairo_operator_t op,
247 const cairo_pattern_t *source,
248 cairo_glyph_t *glyphs,
249 int num_glyphs,
250 cairo_scaled_font_t *scaled_font,
251 cairo_clip_t *clip,
252 int *num_remaining)
253 {
254 *num_remaining = 0;
255 return _cairo_surface_show_text_glyphs (radeon_surface_map_to_image (abstract_surface),
256 op, source,
257 NULL, 0,
258 glyphs, num_glyphs,
259 NULL, 0, 0,
260 scaled_font, clip);
261 }
262
263 static const cairo_surface_backend_t radeon_surface_backend = {
264 CAIRO_SURFACE_TYPE_DRM,
265 _cairo_default_context_create,
266
267 radeon_surface_create_similar,
268 radeon_surface_finish,
269
270 NULL,
271 radeon_surface_acquire_source_image,
272 radeon_surface_release_source_image,
273
274 NULL, NULL, NULL,
275 NULL, /* composite */
276 NULL, /* fill */
277 NULL, /* trapezoids */
278 NULL, /* span */
279 NULL, /* check-span */
280
281 NULL, /* copy_page */
282 NULL, /* show_page */
283 _cairo_drm_surface_get_extents,
284 NULL, /* old-glyphs */
285 _cairo_drm_surface_get_font_options,
286
287 radeon_surface_flush,
288 NULL, /* mark dirty */
289 NULL, NULL, /* font/glyph fini */
290
291 radeon_surface_paint,
292 radeon_surface_mask,
293 radeon_surface_stroke,
294 radeon_surface_fill,
295 radeon_surface_glyphs,
296 };
297
298 static void
radeon_surface_init(radeon_surface_t * surface,cairo_drm_device_t * device,cairo_format_t format,int width,int height)299 radeon_surface_init (radeon_surface_t *surface,
300 cairo_drm_device_t *device,
301 cairo_format_t format,
302 int width, int height)
303 {
304 _cairo_surface_init (&surface->base.base,
305 &radeon_surface_backend,
306 &device->base,
307 _cairo_content_from_format (format),
308 FALSE);
309 _cairo_drm_surface_init (&surface->base, format, width, height);
310 }
311
312 static cairo_surface_t *
radeon_surface_create_internal(cairo_drm_device_t * device,cairo_format_t format,int width,int height)313 radeon_surface_create_internal (cairo_drm_device_t *device,
314 cairo_format_t format,
315 int width, int height)
316 {
317 radeon_surface_t *surface;
318 cairo_status_t status;
319
320 surface = _cairo_malloc (sizeof (radeon_surface_t));
321 if (unlikely (surface == NULL))
322 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
323
324 radeon_surface_init (surface, device, format, width, height);
325
326 if (width && height) {
327 surface->base.stride =
328 cairo_format_stride_for_width (surface->base.format, width);
329
330 surface->base.bo = radeon_bo_create (to_radeon_device (&device->base),
331 surface->base.stride * height,
332 RADEON_GEM_DOMAIN_GTT);
333
334 if (unlikely (surface->base.bo == NULL)) {
335 status = _cairo_drm_surface_finish (&surface->base);
336 free (surface);
337 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
338 }
339 }
340
341 return &surface->base.base;
342 }
343
344 static cairo_surface_t *
radeon_surface_create(cairo_drm_device_t * device,cairo_format_t format,int width,int height)345 radeon_surface_create (cairo_drm_device_t *device,
346 cairo_format_t format,
347 int width, int height)
348 {
349 switch (format) {
350 default:
351 case CAIRO_FORMAT_INVALID:
352 case CAIRO_FORMAT_A1:
353 case CAIRO_FORMAT_RGB16_565:
354 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
355 case CAIRO_FORMAT_ARGB32:
356 case CAIRO_FORMAT_RGB24:
357 case CAIRO_FORMAT_A8:
358 break;
359 }
360
361 return radeon_surface_create_internal (device, format, width, height);
362 }
363
364 static cairo_surface_t *
radeon_surface_create_for_name(cairo_drm_device_t * device,unsigned int name,cairo_format_t format,int width,int height,int stride)365 radeon_surface_create_for_name (cairo_drm_device_t *device,
366 unsigned int name,
367 cairo_format_t format,
368 int width, int height, int stride)
369 {
370 radeon_surface_t *surface;
371 cairo_status_t status;
372
373 switch (format) {
374 default:
375 case CAIRO_FORMAT_INVALID:
376 case CAIRO_FORMAT_A1:
377 case CAIRO_FORMAT_RGB16_565:
378 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
379 case CAIRO_FORMAT_ARGB32:
380 case CAIRO_FORMAT_RGB24:
381 case CAIRO_FORMAT_A8:
382 break;
383 }
384
385 if (stride < cairo_format_stride_for_width (format, width))
386 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
387
388 surface = _cairo_malloc (sizeof (radeon_surface_t));
389 if (unlikely (surface == NULL))
390 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
391
392 radeon_surface_init (surface, device, format, width, height);
393
394 if (width && height) {
395 surface->base.stride = stride;
396
397 surface->base.bo = radeon_bo_create_for_name (to_radeon_device (&device->base),
398 name);
399
400 if (unlikely (surface->base.bo == NULL)) {
401 status = _cairo_drm_surface_finish (&surface->base);
402 free (surface);
403 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
404 }
405 }
406
407 return &surface->base.base;
408 }
409
410 static void
radeon_device_destroy(void * data)411 radeon_device_destroy (void *data)
412 {
413 radeon_device_t *device = data;
414
415 radeon_device_fini (device);
416
417 free (data);
418 }
419
420 cairo_drm_device_t *
_cairo_drm_radeon_device_create(int fd,dev_t dev,int vendor_id,int chip_id)421 _cairo_drm_radeon_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
422 {
423 radeon_device_t *device;
424 uint64_t gart_size, vram_size;
425 cairo_status_t status;
426
427 if (! radeon_info (fd, &gart_size, &vram_size))
428 return NULL;
429
430 device = _cairo_malloc (sizeof (radeon_device_t));
431 if (device == NULL)
432 return _cairo_drm_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
433
434 status = radeon_device_init (device, fd);
435 if (unlikely (status)) {
436 free (device);
437 return _cairo_drm_device_create_in_error (status);
438 }
439
440 device->base.surface.create = radeon_surface_create;
441 device->base.surface.create_for_name = radeon_surface_create_for_name;
442 device->base.surface.create_from_cacheable_image = NULL;
443 device->base.surface.flink = _cairo_drm_surface_flink;
444 device->base.surface.enable_scan_out = NULL;
445
446 device->base.device.flush = NULL;
447 device->base.device.throttle = NULL;
448 device->base.device.destroy = radeon_device_destroy;
449
450 device->vram_limit = vram_size;
451 device->gart_limit = gart_size;
452
453 return _cairo_drm_device_init (&device->base, fd, dev, vendor_id, chip_id, MAX_SIZE);
454 }
455