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