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-intel-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 /* Basic generic/stub surface for intel chipsets */
40 
41 #define MAX_SIZE 2048
42 
43 static cairo_surface_t *
intel_surface_create_similar(void * abstract_surface,cairo_content_t content,int width,int height)44 intel_surface_create_similar (void			*abstract_surface,
45 			      cairo_content_t	 content,
46 			      int			 width,
47 			      int			 height)
48 {
49     return cairo_image_surface_create (_cairo_format_from_content (content),
50 				       width, height);
51 }
52 
53 cairo_status_t
intel_surface_finish(void * abstract_surface)54 intel_surface_finish (void *abstract_surface)
55 {
56     intel_surface_t *surface = abstract_surface;
57 
58     intel_bo_in_flight_add (to_intel_device (surface->drm.base.device),
59 			    to_intel_bo (surface->drm.bo));
60     return _cairo_drm_surface_finish (&surface->drm);
61 }
62 
63 static void
surface_finish_and_destroy(cairo_surface_t * surface)64 surface_finish_and_destroy (cairo_surface_t *surface)
65 {
66     cairo_surface_finish (surface);
67     cairo_surface_destroy (surface);
68 }
69 
70 cairo_status_t
intel_surface_acquire_source_image(void * abstract_surface,cairo_image_surface_t ** image_out,void ** image_extra)71 intel_surface_acquire_source_image (void *abstract_surface,
72 				    cairo_image_surface_t **image_out,
73 				    void **image_extra)
74 {
75     intel_surface_t *surface = abstract_surface;
76     cairo_surface_t *image;
77     cairo_status_t status;
78     void *ptr;
79 
80     if (surface->drm.fallback != NULL) {
81 	image = surface->drm.fallback;
82 	goto DONE;
83     }
84 
85     image = _cairo_surface_has_snapshot (&surface->drm.base,
86 	                                 &_cairo_image_surface_backend);
87     if (image != NULL)
88 	goto DONE;
89 
90     if (surface->drm.base.backend->flush != NULL) {
91 	status = surface->drm.base.backend->flush (surface);
92 	if (unlikely (status))
93 	    return status;
94     }
95 
96     ptr = intel_bo_map (to_intel_device (surface->drm.base.device),
97 			to_intel_bo (surface->drm.bo));
98     if (unlikely (ptr == NULL))
99 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
100 
101     image = cairo_image_surface_create_for_data (ptr,
102 						 surface->drm.format,
103 						 surface->drm.width,
104 						 surface->drm.height,
105 						 surface->drm.stride);
106     if (unlikely (image->status))
107 	return image->status;
108 
109     _cairo_surface_attach_snapshot (&surface->drm.base, image, surface_finish_and_destroy);
110 
111 DONE:
112     *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
113     *image_extra = NULL;
114     return CAIRO_STATUS_SUCCESS;
115 }
116 
117 void
intel_surface_release_source_image(void * abstract_surface,cairo_image_surface_t * image,void * image_extra)118 intel_surface_release_source_image (void *abstract_surface,
119 				    cairo_image_surface_t *image,
120 				    void *image_extra)
121 {
122     cairo_surface_destroy (&image->base);
123 }
124 
125 cairo_surface_t *
intel_surface_map_to_image(void * abstract_surface)126 intel_surface_map_to_image (void *abstract_surface)
127 {
128     intel_surface_t *surface = abstract_surface;
129 
130     if (surface->drm.fallback == NULL) {
131 	cairo_surface_t *image;
132 	cairo_status_t status;
133 	void *ptr;
134 
135 	if (surface->drm.base.backend->flush != NULL) {
136 	    status = surface->drm.base.backend->flush (surface);
137 	    if (unlikely (status))
138 		return _cairo_surface_create_in_error (status);
139 	}
140 
141 	ptr = intel_bo_map (to_intel_device (surface->drm.base.device),
142 			    to_intel_bo (surface->drm.bo));
143 	if (unlikely (ptr == NULL))
144 	    return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
145 
146 	image = cairo_image_surface_create_for_data (ptr,
147 						     surface->drm.format,
148 						     surface->drm.width,
149 						     surface->drm.height,
150 						     surface->drm.stride);
151 	if (unlikely (image->status))
152 	    return image;
153 
154 	surface->drm.fallback = image;
155     }
156 
157     return surface->drm.fallback;
158 }
159 
160 cairo_status_t
intel_surface_flush(void * abstract_surface,unsigned flags)161 intel_surface_flush (void *abstract_surface, unsigned flags)
162 {
163     intel_surface_t *surface = abstract_surface;
164     cairo_status_t status;
165 
166     if (flags)
167 	return CAIRO_STATUS_SUCCESS;
168 
169     if (surface->drm.fallback == NULL)
170 	return CAIRO_STATUS_SUCCESS;
171 
172     /* kill any outstanding maps */
173     cairo_surface_finish (surface->drm.fallback);
174 
175     status = cairo_surface_status (surface->drm.fallback);
176     cairo_surface_destroy (surface->drm.fallback);
177     surface->drm.fallback = NULL;
178 
179     return status;
180 }
181 
182 static cairo_int_status_t
intel_surface_paint(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,cairo_clip_t * clip)183 intel_surface_paint (void *abstract_surface,
184 		     cairo_operator_t		 op,
185 		     const cairo_pattern_t	*source,
186 		     cairo_clip_t		*clip)
187 {
188     return _cairo_surface_paint (intel_surface_map_to_image (abstract_surface),
189 				 op, source, clip);
190 }
191 
192 static cairo_int_status_t
intel_surface_mask(void * abstract_surface,cairo_operator_t op,const cairo_pattern_t * source,const cairo_pattern_t * mask,cairo_clip_t * clip)193 intel_surface_mask (void			*abstract_surface,
194 		    cairo_operator_t		 op,
195 		    const cairo_pattern_t	*source,
196 		    const cairo_pattern_t	*mask,
197 		    cairo_clip_t		*clip)
198 {
199     return _cairo_surface_mask (intel_surface_map_to_image (abstract_surface),
200 				op, source, mask, clip);
201 }
202 
203 static cairo_int_status_t
intel_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)204 intel_surface_stroke (void			*abstract_surface,
205 		      cairo_operator_t		 op,
206 		      const cairo_pattern_t	*source,
207 		      cairo_path_fixed_t	*path,
208 		      const cairo_stroke_style_t	*stroke_style,
209 		      const cairo_matrix_t		*ctm,
210 		      const cairo_matrix_t		*ctm_inverse,
211 		      double			 tolerance,
212 		      cairo_antialias_t		 antialias,
213 		      cairo_clip_t		*clip)
214 {
215     return _cairo_surface_stroke (intel_surface_map_to_image (abstract_surface),
216 				  op, source, path, stroke_style, ctm, ctm_inverse,
217 				  tolerance, antialias, clip);
218 }
219 
220 static cairo_int_status_t
intel_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)221 intel_surface_fill (void			*abstract_surface,
222 		    cairo_operator_t		 op,
223 		    const cairo_pattern_t	*source,
224 		    cairo_path_fixed_t		*path,
225 		    cairo_fill_rule_t		 fill_rule,
226 		    double			 tolerance,
227 		    cairo_antialias_t		 antialias,
228 		    cairo_clip_t		*clip)
229 {
230     return _cairo_surface_fill (intel_surface_map_to_image (abstract_surface),
231 				op, source, path, fill_rule,
232 				tolerance, antialias, clip);
233 }
234 
235 static cairo_int_status_t
intel_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)236 intel_surface_glyphs (void			*abstract_surface,
237 		      cairo_operator_t		 op,
238 		      const cairo_pattern_t	*source,
239 		      cairo_glyph_t		*glyphs,
240 		      int			 num_glyphs,
241 		      cairo_scaled_font_t	*scaled_font,
242 		      cairo_clip_t		*clip,
243 		      int *num_remaining)
244 {
245     *num_remaining = 0;
246     return _cairo_surface_show_text_glyphs (intel_surface_map_to_image (abstract_surface),
247 					    op, source,
248 					    NULL, 0,
249 					    glyphs, num_glyphs,
250 					    NULL, 0, 0,
251 					    scaled_font, clip);
252 }
253 
254 static const cairo_surface_backend_t intel_surface_backend = {
255     CAIRO_SURFACE_TYPE_DRM,
256     _cairo_default_context_create,
257 
258     intel_surface_create_similar,
259     intel_surface_finish,
260 
261     NULL,
262     intel_surface_acquire_source_image,
263     intel_surface_release_source_image,
264 
265     NULL, NULL, NULL,
266     NULL, /* composite */
267     NULL, /* fill */
268     NULL, /* trapezoids */
269     NULL, /* span */
270     NULL, /* check-span */
271 
272     NULL, /* copy_page */
273     NULL, /* show_page */
274     _cairo_drm_surface_get_extents,
275     NULL, /* old-glyphs */
276     _cairo_drm_surface_get_font_options,
277 
278     intel_surface_flush,
279     NULL, /* mark dirty */
280     NULL, NULL, /* font/glyph fini */
281 
282     intel_surface_paint,
283     intel_surface_mask,
284     intel_surface_stroke,
285     intel_surface_fill,
286     intel_surface_glyphs,
287 };
288 
289 void
intel_surface_init(intel_surface_t * surface,const cairo_surface_backend_t * backend,cairo_drm_device_t * device,cairo_format_t format,int width,int height)290 intel_surface_init (intel_surface_t *surface,
291 		    const cairo_surface_backend_t *backend,
292 		    cairo_drm_device_t *device,
293 		    cairo_format_t format,
294 		    int width, int height)
295 {
296     _cairo_surface_init (&surface->drm.base,
297 			 backend,
298 			 &device->base,
299 			 _cairo_content_from_format (format),
300 			 FALSE);
301 
302     _cairo_drm_surface_init (&surface->drm, format, width, height);
303 
304     surface->snapshot_cache_entry.hash = 0;
305 }
306 
307 static cairo_surface_t *
intel_surface_create(cairo_drm_device_t * device,cairo_format_t format,int width,int height)308 intel_surface_create (cairo_drm_device_t *device,
309 		      cairo_format_t format,
310 		      int width, int height)
311 {
312     intel_surface_t *surface;
313     cairo_status_t status;
314 
315     surface = _cairo_malloc (sizeof (intel_surface_t));
316     if (unlikely (surface == NULL))
317 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
318 
319     intel_surface_init (surface, &intel_surface_backend, device,
320 			format, width, height);
321 
322     if (width && height) {
323 	/* Vol I, p134: size restrictions for textures */
324 	width  = (width  + 3) & -4;
325 	height = (height + 1) & -2;
326 	surface->drm.stride =
327 	    cairo_format_stride_for_width (surface->drm.format, width);
328 	surface->drm.bo = &intel_bo_create (to_intel_device (&device->base),
329 					    surface->drm.stride * height,
330 					    surface->drm.stride * height,
331 					    TRUE, I915_TILING_NONE, surface->drm.stride)->base;
332 	if (surface->drm.bo == NULL) {
333 	    status = _cairo_drm_surface_finish (&surface->drm);
334 	    free (surface);
335 	    return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
336 	}
337     }
338 
339     return &surface->drm.base;
340 }
341 
342 static cairo_surface_t *
intel_surface_create_for_name(cairo_drm_device_t * device,unsigned int name,cairo_format_t format,int width,int height,int stride)343 intel_surface_create_for_name (cairo_drm_device_t *device,
344 			       unsigned int name,
345 			       cairo_format_t format,
346 			       int width, int height, int stride)
347 {
348     intel_surface_t *surface;
349     cairo_status_t status;
350 
351     switch (format) {
352     default:
353     case CAIRO_FORMAT_INVALID:
354     case CAIRO_FORMAT_A1:
355 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
356     case CAIRO_FORMAT_ARGB32:
357     case CAIRO_FORMAT_RGB16_565:
358     case CAIRO_FORMAT_RGB24:
359     case CAIRO_FORMAT_A8:
360 	break;
361     }
362 
363     if (stride < cairo_format_stride_for_width (format, width))
364 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
365 
366     surface = _cairo_malloc (sizeof (intel_surface_t));
367     if (unlikely (surface == NULL))
368 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
369 
370     intel_surface_init (surface, &intel_surface_backend,
371 			device, format, width, height);
372 
373     if (width && height) {
374 	surface->drm.stride = stride;
375 
376 	surface->drm.bo = &intel_bo_create_for_name (to_intel_device (&device->base),
377 						      name)->base;
378 	if (unlikely (surface->drm.bo == NULL)) {
379 	    status = _cairo_drm_surface_finish (&surface->drm);
380 	    free (surface);
381 	    return _cairo_surface_create_in_error (_cairo_error
382 						   (CAIRO_STATUS_NO_MEMORY));
383 	}
384     }
385 
386     return &surface->drm.base;
387 }
388 
389 static cairo_status_t
intel_surface_enable_scan_out(void * abstract_surface)390 intel_surface_enable_scan_out (void *abstract_surface)
391 {
392     intel_surface_t *surface = abstract_surface;
393 
394     if (unlikely (surface->drm.bo == NULL))
395 	return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
396 
397     to_intel_bo (surface->drm.bo)->tiling = I915_TILING_X;
398 
399     return CAIRO_STATUS_SUCCESS;
400 }
401 
402 static cairo_int_status_t
intel_device_throttle(cairo_drm_device_t * device)403 intel_device_throttle (cairo_drm_device_t *device)
404 {
405     intel_throttle (to_intel_device (&device->base));
406     return CAIRO_STATUS_SUCCESS;
407 }
408 
409 static void
intel_device_destroy(void * data)410 intel_device_destroy (void *data)
411 {
412     intel_device_t *device = data;
413 
414     intel_device_fini (device);
415 
416     free (data);
417 }
418 
419 cairo_drm_device_t *
_cairo_drm_intel_device_create(int fd,dev_t dev,int vendor_id,int chip_id)420 _cairo_drm_intel_device_create (int fd, dev_t dev, int vendor_id, int chip_id)
421 {
422     intel_device_t *device;
423     cairo_status_t status;
424 
425     if (! intel_info (fd, NULL))
426 	return NULL;
427 
428     device = _cairo_malloc (sizeof (intel_device_t));
429     if (unlikely (device == NULL))
430 	return (cairo_drm_device_t *) _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
431 
432     status = intel_device_init (device, fd);
433     if (unlikely (status)) {
434 	free (device);
435 	return (cairo_drm_device_t *) _cairo_device_create_in_error (status);
436     }
437 
438     device->base.surface.create = intel_surface_create;
439     device->base.surface.create_for_name = intel_surface_create_for_name;
440     device->base.surface.create_from_cacheable_image = NULL;
441     device->base.surface.flink = _cairo_drm_surface_flink;
442     device->base.surface.enable_scan_out = intel_surface_enable_scan_out;
443 
444     device->base.surface.map_to_image = intel_surface_map_to_image;
445 
446     device->base.device.flush = NULL;
447     device->base.device.throttle = intel_device_throttle;
448     device->base.device.destroy = intel_device_destroy;
449 
450     return _cairo_drm_device_init (&device->base,
451 				   fd, dev,
452 				   vendor_id, chip_id,
453 				   MAX_SIZE);
454 }
455