1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsIMemoryReporter.h"
7 #include "nsMemory.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/Base64.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "nsISupportsImpl.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/gfx/Logging.h"
16 #include "mozilla/gfx/HelpersCairo.h"
17 #include "gfx2DGlue.h"
18
19 #include "gfxASurface.h"
20 #include "gfxContext.h"
21 #include "gfxImageSurface.h"
22 #include "gfxPlatform.h"
23 #include "gfxRect.h"
24
25 #include "cairo.h"
26 #include <algorithm>
27
28 #ifdef CAIRO_HAS_WIN32_SURFACE
29 # include "gfxWindowsSurface.h"
30 #endif
31
32 #ifdef MOZ_X11
33 # include "gfxXlibSurface.h"
34 #endif
35
36 #ifdef CAIRO_HAS_QUARTZ_SURFACE
37 # include "gfxQuartzSurface.h"
38 #endif
39
40 #include <stdio.h>
41 #include <limits.h>
42
43 #include "nsComponentManagerUtils.h"
44 #include "nsISupportsUtils.h"
45 #include "nsCOMPtr.h"
46 #include "nsServiceManagerUtils.h"
47 #include "nsString.h"
48
49 using namespace mozilla;
50 using namespace mozilla::gfx;
51
52 static cairo_user_data_key_t gfxasurface_pointer_key;
53
gfxASurface()54 gfxASurface::gfxASurface()
55 : mSurface(nullptr),
56 mFloatingRefs(0),
57 mBytesRecorded(0),
58 mSurfaceValid(false) {
59 MOZ_COUNT_CTOR(gfxASurface);
60 }
61
~gfxASurface()62 gfxASurface::~gfxASurface() {
63 RecordMemoryFreed();
64
65 MOZ_COUNT_DTOR(gfxASurface);
66 }
67
68 // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
69 // refcount mismatch issues.
AddRef(void)70 nsrefcnt gfxASurface::AddRef(void) {
71 if (mSurfaceValid) {
72 if (mFloatingRefs) {
73 // eat a floating ref
74 mFloatingRefs--;
75 } else {
76 cairo_surface_reference(mSurface);
77 }
78
79 return (nsrefcnt)cairo_surface_get_reference_count(mSurface);
80 }
81 // the surface isn't valid, but we still need to refcount
82 // the gfxASurface
83 return ++mFloatingRefs;
84 }
85
Release(void)86 nsrefcnt gfxASurface::Release(void) {
87 if (mSurfaceValid) {
88 NS_ASSERTION(
89 mFloatingRefs == 0,
90 "gfxASurface::Release with floating refs still hanging around!");
91
92 // Note that there is a destructor set on user data for mSurface,
93 // which will delete this gfxASurface wrapper when the surface's refcount
94 // goes out of scope.
95 nsrefcnt refcnt = (nsrefcnt)cairo_surface_get_reference_count(mSurface);
96 cairo_surface_destroy(mSurface);
97
98 // |this| may not be valid any more, don't use it!
99
100 return --refcnt;
101 }
102 if (--mFloatingRefs == 0) {
103 delete this;
104 return 0;
105 }
106 return mFloatingRefs;
107 }
108
SurfaceDestroyFunc(void * data)109 void gfxASurface::SurfaceDestroyFunc(void* data) {
110 gfxASurface* surf = (gfxASurface*)data;
111 // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface,
112 // data);
113 delete surf;
114 }
115
GetSurfaceWrapper(cairo_surface_t * csurf)116 gfxASurface* gfxASurface::GetSurfaceWrapper(cairo_surface_t* csurf) {
117 if (!csurf) return nullptr;
118 return (gfxASurface*)cairo_surface_get_user_data(csurf,
119 &gfxasurface_pointer_key);
120 }
121
SetSurfaceWrapper(cairo_surface_t * csurf,gfxASurface * asurf)122 void gfxASurface::SetSurfaceWrapper(cairo_surface_t* csurf,
123 gfxASurface* asurf) {
124 if (!csurf) return;
125 cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf,
126 SurfaceDestroyFunc);
127 }
128
Wrap(cairo_surface_t * csurf,const IntSize & aSize)129 already_AddRefed<gfxASurface> gfxASurface::Wrap(cairo_surface_t* csurf,
130 const IntSize& aSize) {
131 RefPtr<gfxASurface> result;
132
133 /* Do we already have a wrapper for this surface? */
134 result = GetSurfaceWrapper(csurf);
135 if (result) {
136 // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
137 return result.forget();
138 }
139
140 /* No wrapper; figure out the surface type and create it */
141 cairo_surface_type_t stype = cairo_surface_get_type(csurf);
142
143 if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
144 result = new gfxImageSurface(csurf);
145 }
146 #ifdef CAIRO_HAS_WIN32_SURFACE
147 else if (stype == CAIRO_SURFACE_TYPE_WIN32 ||
148 stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
149 result = new gfxWindowsSurface(csurf);
150 }
151 #endif
152 #ifdef MOZ_X11
153 else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
154 result = new gfxXlibSurface(csurf);
155 }
156 #endif
157 #ifdef CAIRO_HAS_QUARTZ_SURFACE
158 else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
159 result = new gfxQuartzSurface(csurf, aSize);
160 }
161 #endif
162 else {
163 result = new gfxUnknownSurface(csurf, aSize);
164 }
165
166 // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
167
168 return result.forget();
169 }
170
Init(cairo_surface_t * surface,bool existingSurface)171 void gfxASurface::Init(cairo_surface_t* surface, bool existingSurface) {
172 SetSurfaceWrapper(surface, this);
173 MOZ_ASSERT(surface, "surface should be a valid pointer");
174
175 mSurface = surface;
176 mSurfaceValid = !cairo_surface_status(surface);
177 if (!mSurfaceValid) {
178 gfxWarning() << "ASurface Init failed with Cairo status "
179 << cairo_surface_status(surface) << " on " << hexa(surface);
180 }
181
182 if (existingSurface || !mSurfaceValid) {
183 mFloatingRefs = 0;
184 } else {
185 mFloatingRefs = 1;
186 #ifdef MOZ_TREE_CAIRO
187 if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
188 cairo_surface_set_subpixel_antialiasing(
189 surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
190 }
191 #endif
192 }
193 }
194
GetType() const195 gfxSurfaceType gfxASurface::GetType() const {
196 if (!mSurfaceValid) return (gfxSurfaceType)-1;
197
198 return (gfxSurfaceType)cairo_surface_get_type(mSurface);
199 }
200
GetContentType() const201 gfxContentType gfxASurface::GetContentType() const {
202 if (!mSurfaceValid) return (gfxContentType)-1;
203
204 return (gfxContentType)cairo_surface_get_content(mSurface);
205 }
206
SetDeviceOffset(const gfxPoint & offset)207 void gfxASurface::SetDeviceOffset(const gfxPoint& offset) {
208 if (!mSurfaceValid) return;
209 cairo_surface_set_device_offset(mSurface, offset.x, offset.y);
210 }
211
GetDeviceOffset() const212 gfxPoint gfxASurface::GetDeviceOffset() const {
213 if (!mSurfaceValid) return gfxPoint(0.0, 0.0);
214 gfxPoint pt;
215 cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y);
216 return pt;
217 }
218
Flush() const219 void gfxASurface::Flush() const {
220 if (!mSurfaceValid) return;
221 cairo_surface_flush(mSurface);
222 gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
223 }
224
MarkDirty()225 void gfxASurface::MarkDirty() {
226 if (!mSurfaceValid) return;
227 cairo_surface_mark_dirty(mSurface);
228 gfxPlatform::ClearSourceSurfaceForSurface(this);
229 }
230
MarkDirty(const gfxRect & r)231 void gfxASurface::MarkDirty(const gfxRect& r) {
232 if (!mSurfaceValid) return;
233 cairo_surface_mark_dirty_rectangle(mSurface, (int)r.X(), (int)r.Y(),
234 (int)r.Width(), (int)r.Height());
235 gfxPlatform::ClearSourceSurfaceForSurface(this);
236 }
237
SetData(const cairo_user_data_key_t * key,void * user_data,thebes_destroy_func_t destroy)238 void gfxASurface::SetData(const cairo_user_data_key_t* key, void* user_data,
239 thebes_destroy_func_t destroy) {
240 if (!mSurfaceValid) return;
241 cairo_surface_set_user_data(mSurface, key, user_data, destroy);
242 }
243
GetData(const cairo_user_data_key_t * key)244 void* gfxASurface::GetData(const cairo_user_data_key_t* key) {
245 if (!mSurfaceValid) return nullptr;
246 return cairo_surface_get_user_data(mSurface, key);
247 }
248
Finish()249 void gfxASurface::Finish() {
250 // null surfaces are allowed here
251 cairo_surface_finish(mSurface);
252 }
253
CreateSimilarSurface(gfxContentType aContent,const IntSize & aSize)254 already_AddRefed<gfxASurface> gfxASurface::CreateSimilarSurface(
255 gfxContentType aContent, const IntSize& aSize) {
256 if (!mSurface || !mSurfaceValid) {
257 return nullptr;
258 }
259
260 cairo_surface_t* surface = cairo_surface_create_similar(
261 mSurface, cairo_content_t(int(aContent)), aSize.width, aSize.height);
262 if (cairo_surface_status(surface)) {
263 cairo_surface_destroy(surface);
264 return nullptr;
265 }
266
267 RefPtr<gfxASurface> result = Wrap(surface, aSize);
268 cairo_surface_destroy(surface);
269 return result.forget();
270 }
271
CopyToARGB32ImageSurface()272 already_AddRefed<gfxImageSurface> gfxASurface::CopyToARGB32ImageSurface() {
273 if (!mSurface || !mSurfaceValid) {
274 return nullptr;
275 }
276
277 const IntSize size = GetSize();
278 RefPtr<gfxImageSurface> imgSurface =
279 new gfxImageSurface(size, SurfaceFormat::A8R8G8B8_UINT32);
280
281 RefPtr<DrawTarget> dt = gfxPlatform::CreateDrawTargetForSurface(
282 imgSurface, IntSize(size.width, size.height));
283 RefPtr<SourceSurface> source =
284 gfxPlatform::GetSourceSurfaceForSurface(dt, this);
285
286 dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
287
288 return imgSurface.forget();
289 }
290
CairoStatus()291 int gfxASurface::CairoStatus() {
292 if (!mSurfaceValid) return -1;
293
294 return cairo_surface_status(mSurface);
295 }
296
BeginPrinting(const nsAString & aTitle,const nsAString & aPrintToFileName)297 nsresult gfxASurface::BeginPrinting(const nsAString& aTitle,
298 const nsAString& aPrintToFileName) {
299 return NS_OK;
300 }
301
EndPrinting()302 nsresult gfxASurface::EndPrinting() { return NS_OK; }
303
AbortPrinting()304 nsresult gfxASurface::AbortPrinting() { return NS_OK; }
305
BeginPage()306 nsresult gfxASurface::BeginPage() { return NS_OK; }
307
EndPage()308 nsresult gfxASurface::EndPage() { return NS_OK; }
309
ContentFromFormat(gfxImageFormat format)310 gfxContentType gfxASurface::ContentFromFormat(gfxImageFormat format) {
311 switch (format) {
312 case SurfaceFormat::A8R8G8B8_UINT32:
313 return gfxContentType::COLOR_ALPHA;
314 case SurfaceFormat::X8R8G8B8_UINT32:
315 case SurfaceFormat::R5G6B5_UINT16:
316 return gfxContentType::COLOR;
317 case SurfaceFormat::A8:
318 return gfxContentType::ALPHA;
319
320 case SurfaceFormat::UNKNOWN:
321 default:
322 return gfxContentType::COLOR;
323 }
324 }
325
BytePerPixelFromFormat(gfxImageFormat format)326 int32_t gfxASurface::BytePerPixelFromFormat(gfxImageFormat format) {
327 switch (format) {
328 case SurfaceFormat::A8R8G8B8_UINT32:
329 case SurfaceFormat::X8R8G8B8_UINT32:
330 return 4;
331 case SurfaceFormat::R5G6B5_UINT16:
332 return 2;
333 case SurfaceFormat::A8:
334 return 1;
335 default:
336 NS_WARNING("Unknown byte per pixel value for Image format");
337 }
338 return 0;
339 }
340
341 /** Memory reporting **/
342
343 static const char* sDefaultSurfaceDescription =
344 "Memory used by gfx surface of the given type.";
345
346 struct SurfaceMemoryReporterAttrs {
347 const char* path;
348 const char* description;
349 };
350
351 static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
352 {"gfx-surface-image", nullptr},
353 {"gfx-surface-pdf", nullptr},
354 {"gfx-surface-ps", nullptr},
355 {"gfx-surface-xlib",
356 "Memory used by xlib surfaces to store pixmaps. This memory lives in "
357 "the X server's process rather than in this application, so the bytes "
358 "accounted for here aren't counted in vsize, resident, explicit, or any "
359 "of "
360 "the other measurements on this page."},
361 {"gfx-surface-xcb", nullptr},
362 {"gfx-surface-glitz???", nullptr}, // should never be used
363 {"gfx-surface-quartz", nullptr},
364 {"gfx-surface-win32", nullptr},
365 {"gfx-surface-beos", nullptr},
366 {"gfx-surface-directfb???", nullptr}, // should never be used
367 {"gfx-surface-svg", nullptr},
368 {"gfx-surface-os2", nullptr},
369 {"gfx-surface-win32printing", nullptr},
370 {"gfx-surface-quartzimage", nullptr},
371 {"gfx-surface-script", nullptr},
372 {"gfx-surface-qpainter", nullptr},
373 {"gfx-surface-recording", nullptr},
374 {"gfx-surface-vg", nullptr},
375 {"gfx-surface-gl", nullptr},
376 {"gfx-surface-drm", nullptr},
377 {"gfx-surface-tee", nullptr},
378 {"gfx-surface-xml", nullptr},
379 {"gfx-surface-skia", nullptr},
380 {"gfx-surface-subsurface", nullptr},
381 };
382
383 static_assert(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) ==
384 size_t(gfxSurfaceType::Max),
385 "sSurfaceMemoryReporterAttrs exceeds max capacity");
386 static_assert(uint32_t(CAIRO_SURFACE_TYPE_SKIA) ==
387 uint32_t(gfxSurfaceType::Skia),
388 "CAIRO_SURFACE_TYPE_SKIA not equal to gfxSurfaceType::Skia");
389
390 /* Surface size memory reporting */
391
392 class SurfaceMemoryReporter final : public nsIMemoryReporter {
393 ~SurfaceMemoryReporter() = default;
394
395 // We can touch this array on several different threads, and we don't
396 // want to introduce memory barriers when recording the memory used. To
397 // assure dynamic race checkers like TSan that this is OK, we use
398 // relaxed memory ordering here.
399 static Atomic<size_t, Relaxed>
400 sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
401
402 public:
AdjustUsedMemory(gfxSurfaceType aType,int32_t aBytes)403 static void AdjustUsedMemory(gfxSurfaceType aType, int32_t aBytes) {
404 // A read-modify-write operation like += would require a memory barrier
405 // here, which would defeat the purpose of using relaxed memory
406 // ordering. So separate out the read and write operations.
407 sSurfaceMemoryUsed[size_t(aType)] =
408 sSurfaceMemoryUsed[size_t(aType)] + aBytes;
409 };
410
411 // This memory reporter is sometimes allocated on the compositor thread,
412 // but always released on the main thread, so its refcounting needs to be
413 // threadsafe.
414 NS_DECL_THREADSAFE_ISUPPORTS
415
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)416 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
417 nsISupports* aData, bool aAnonymize) override {
418 const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
419 for (size_t i = 0; i < len; i++) {
420 int64_t amount = sSurfaceMemoryUsed[i];
421
422 if (amount != 0) {
423 const char* path = sSurfaceMemoryReporterAttrs[i].path;
424 const char* desc = sSurfaceMemoryReporterAttrs[i].description;
425 if (!desc) {
426 desc = sDefaultSurfaceDescription;
427 }
428
429 aHandleReport->Callback(""_ns, nsCString(path), KIND_OTHER, UNITS_BYTES,
430 amount, nsCString(desc), aData);
431 }
432 }
433
434 return NS_OK;
435 }
436 };
437
438 Atomic<size_t, Relaxed>
439 SurfaceMemoryReporter::sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
440
NS_IMPL_ISUPPORTS(SurfaceMemoryReporter,nsIMemoryReporter)441 NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
442
443 void gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
444 int32_t aBytes) {
445 if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
446 NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
447 return;
448 }
449
450 static bool registered = false;
451 if (!registered) {
452 RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
453 registered = true;
454 }
455
456 SurfaceMemoryReporter::AdjustUsedMemory(aType, aBytes);
457 }
458
RecordMemoryUsed(int32_t aBytes)459 void gfxASurface::RecordMemoryUsed(int32_t aBytes) {
460 RecordMemoryUsedForSurfaceType(GetType(), aBytes);
461 mBytesRecorded += aBytes;
462 }
463
RecordMemoryFreed()464 void gfxASurface::RecordMemoryFreed() {
465 if (mBytesRecorded) {
466 RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
467 mBytesRecorded = 0;
468 }
469 }
470
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const471 size_t gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
472 // We don't measure mSurface because cairo doesn't allow it.
473 return 0;
474 }
475
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const476 size_t gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
477 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
478 }
479
480 /* static */
BytesPerPixel(gfxImageFormat aImageFormat)481 uint8_t gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat) {
482 switch (aImageFormat) {
483 case SurfaceFormat::A8R8G8B8_UINT32:
484 return 4;
485 case SurfaceFormat::X8R8G8B8_UINT32:
486 return 4;
487 case SurfaceFormat::R5G6B5_UINT16:
488 return 2;
489 case SurfaceFormat::A8:
490 return 1;
491 case SurfaceFormat::UNKNOWN:
492 default:
493 MOZ_ASSERT_UNREACHABLE("Not really sure what you want me to say here");
494 return 0;
495 }
496 }
497
SetOpaqueRect(const gfxRect & aRect)498 void gfxASurface::SetOpaqueRect(const gfxRect& aRect) {
499 if (aRect.IsEmpty()) {
500 mOpaqueRect = nullptr;
501 } else if (!!mOpaqueRect) {
502 *mOpaqueRect = aRect;
503 } else {
504 mOpaqueRect = MakeUnique<gfxRect>(aRect);
505 }
506 }
507
GetEmptyOpaqueRect()508 /* static */ const gfxRect& gfxASurface::GetEmptyOpaqueRect() {
509 static const gfxRect empty(0, 0, 0, 0);
510 return empty;
511 }
512
GetSize() const513 const IntSize gfxASurface::GetSize() const { return IntSize(-1, -1); }
514
GetSurfaceFormat() const515 SurfaceFormat gfxASurface::GetSurfaceFormat() const {
516 if (!mSurfaceValid) {
517 return SurfaceFormat::UNKNOWN;
518 }
519 return GfxFormatForCairoSurface(mSurface);
520 }
521
GetAsImageSurface()522 already_AddRefed<gfxImageSurface> gfxASurface::GetAsImageSurface() {
523 return nullptr;
524 }
525