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/. */
6 #include "nsIMemoryReporter.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"
19 #include "gfxASurface.h"
20 #include "gfxContext.h"
21 #include "gfxImageSurface.h"
22 #include "gfxPlatform.h"
28 #ifdef CAIRO_HAS_WIN32_SURFACE
29 # include "gfxWindowsSurface.h"
33 # include "gfxXlibSurface.h"
36 #ifdef CAIRO_HAS_QUARTZ_SURFACE
37 # include "gfxQuartzSurface.h"
43 #include "nsComponentManagerUtils.h"
44 #include "nsISupportsUtils.h"
46 #include "nsServiceManagerUtils.h"
49 using namespace mozilla
;
50 using namespace mozilla::gfx
;
52 static cairo_user_data_key_t gfxasurface_pointer_key
;
54 gfxASurface::gfxASurface()
58 mSurfaceValid(false) {
59 MOZ_COUNT_CTOR(gfxASurface
);
62 gfxASurface::~gfxASurface() {
65 MOZ_COUNT_DTOR(gfxASurface
);
68 // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
69 // refcount mismatch issues.
70 nsrefcnt
gfxASurface::AddRef(void) {
76 cairo_surface_reference(mSurface
);
79 return (nsrefcnt
)cairo_surface_get_reference_count(mSurface
);
81 // the surface isn't valid, but we still need to refcount
83 return ++mFloatingRefs
;
86 nsrefcnt
gfxASurface::Release(void) {
90 "gfxASurface::Release with floating refs still hanging around!");
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
95 nsrefcnt refcnt
= (nsrefcnt
)cairo_surface_get_reference_count(mSurface
);
96 cairo_surface_destroy(mSurface
);
98 // |this| may not be valid any more, don't use it!
102 if (--mFloatingRefs
== 0) {
106 return mFloatingRefs
;
109 void gfxASurface::SurfaceDestroyFunc(void* data
) {
110 gfxASurface
* surf
= (gfxASurface
*)data
;
111 // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface,
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
);
122 void gfxASurface::SetSurfaceWrapper(cairo_surface_t
* csurf
,
123 gfxASurface
* asurf
) {
125 cairo_surface_set_user_data(csurf
, &gfxasurface_pointer_key
, asurf
,
129 already_AddRefed
<gfxASurface
> gfxASurface::Wrap(cairo_surface_t
* csurf
,
130 const IntSize
& aSize
) {
131 RefPtr
<gfxASurface
> result
;
133 /* Do we already have a wrapper for this surface? */
134 result
= GetSurfaceWrapper(csurf
);
136 // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
137 return result
.forget();
140 /* No wrapper; figure out the surface type and create it */
141 cairo_surface_type_t stype
= cairo_surface_get_type(csurf
);
143 if (stype
== CAIRO_SURFACE_TYPE_IMAGE
) {
144 result
= new gfxImageSurface(csurf
);
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
);
153 else if (stype
== CAIRO_SURFACE_TYPE_XLIB
) {
154 result
= new gfxXlibSurface(csurf
);
157 #ifdef CAIRO_HAS_QUARTZ_SURFACE
158 else if (stype
== CAIRO_SURFACE_TYPE_QUARTZ
) {
159 result
= new gfxQuartzSurface(csurf
, aSize
);
163 result
= new gfxUnknownSurface(csurf
, aSize
);
166 // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
168 return result
.forget();
171 void gfxASurface::Init(cairo_surface_t
* surface
, bool existingSurface
) {
172 SetSurfaceWrapper(surface
, this);
173 MOZ_ASSERT(surface
, "surface should be a valid pointer");
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
);
182 if (existingSurface
|| !mSurfaceValid
) {
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
);
195 gfxSurfaceType
gfxASurface::GetType() const {
196 if (!mSurfaceValid
) return (gfxSurfaceType
)-1;
198 return (gfxSurfaceType
)cairo_surface_get_type(mSurface
);
201 gfxContentType
gfxASurface::GetContentType() const {
202 if (!mSurfaceValid
) return (gfxContentType
)-1;
204 return (gfxContentType
)cairo_surface_get_content(mSurface
);
207 void gfxASurface::SetDeviceOffset(const gfxPoint
& offset
) {
208 if (!mSurfaceValid
) return;
209 cairo_surface_set_device_offset(mSurface
, offset
.x
, offset
.y
);
212 gfxPoint
gfxASurface::GetDeviceOffset() const {
213 if (!mSurfaceValid
) return gfxPoint(0.0, 0.0);
215 cairo_surface_get_device_offset(mSurface
, &pt
.x
, &pt
.y
);
219 void gfxASurface::Flush() const {
220 if (!mSurfaceValid
) return;
221 cairo_surface_flush(mSurface
);
222 gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface
*>(this));
225 void gfxASurface::MarkDirty() {
226 if (!mSurfaceValid
) return;
227 cairo_surface_mark_dirty(mSurface
);
228 gfxPlatform::ClearSourceSurfaceForSurface(this);
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);
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
);
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
);
249 void gfxASurface::Finish() {
250 // null surfaces are allowed here
251 cairo_surface_finish(mSurface
);
254 already_AddRefed
<gfxASurface
> gfxASurface::CreateSimilarSurface(
255 gfxContentType aContent
, const IntSize
& aSize
) {
256 if (!mSurface
|| !mSurfaceValid
) {
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
);
267 RefPtr
<gfxASurface
> result
= Wrap(surface
, aSize
);
268 cairo_surface_destroy(surface
);
269 return result
.forget();
272 already_AddRefed
<gfxImageSurface
> gfxASurface::CopyToARGB32ImageSurface() {
273 if (!mSurface
|| !mSurfaceValid
) {
277 const IntSize size
= GetSize();
278 RefPtr
<gfxImageSurface
> imgSurface
=
279 new gfxImageSurface(size
, SurfaceFormat::A8R8G8B8_UINT32
);
281 RefPtr
<DrawTarget
> dt
= gfxPlatform::CreateDrawTargetForSurface(
282 imgSurface
, IntSize(size
.width
, size
.height
));
283 RefPtr
<SourceSurface
> source
=
284 gfxPlatform::GetSourceSurfaceForSurface(dt
, this);
286 dt
->CopySurface(source
, IntRect(0, 0, size
.width
, size
.height
), IntPoint());
288 return imgSurface
.forget();
291 int gfxASurface::CairoStatus() {
292 if (!mSurfaceValid
) return -1;
294 return cairo_surface_status(mSurface
);
297 nsresult
gfxASurface::BeginPrinting(const nsAString
& aTitle
,
298 const nsAString
& aPrintToFileName
) {
302 nsresult
gfxASurface::EndPrinting() { return NS_OK
; }
304 nsresult
gfxASurface::AbortPrinting() { return NS_OK
; }
306 nsresult
gfxASurface::BeginPage() { return NS_OK
; }
308 nsresult
gfxASurface::EndPage() { return NS_OK
; }
310 gfxContentType
gfxASurface::ContentFromFormat(gfxImageFormat 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
;
320 case SurfaceFormat::UNKNOWN
:
322 return gfxContentType::COLOR
;
326 int32_t gfxASurface::BytePerPixelFromFormat(gfxImageFormat format
) {
328 case SurfaceFormat::A8R8G8B8_UINT32
:
329 case SurfaceFormat::X8R8G8B8_UINT32
:
331 case SurfaceFormat::R5G6B5_UINT16
:
333 case SurfaceFormat::A8
:
336 NS_WARNING("Unknown byte per pixel value for Image format");
341 /** Memory reporting **/
343 static const char* sDefaultSurfaceDescription
=
344 "Memory used by gfx surface of the given type.";
346 struct SurfaceMemoryReporterAttrs
{
348 const char* description
;
351 static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs
[] = {
352 {"gfx-surface-image", nullptr},
353 {"gfx-surface-pdf", nullptr},
354 {"gfx-surface-ps", nullptr},
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 "
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},
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");
390 /* Surface size memory reporting */
392 class SurfaceMemoryReporter final
: public nsIMemoryReporter
{
393 ~SurfaceMemoryReporter() = default;
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
)];
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
;
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
414 NS_DECL_THREADSAFE_ISUPPORTS
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
];
423 const char* path
= sSurfaceMemoryReporterAttrs
[i
].path
;
424 const char* desc
= sSurfaceMemoryReporterAttrs
[i
].description
;
426 desc
= sDefaultSurfaceDescription
;
429 aHandleReport
->Callback(""_ns
, nsCString(path
), KIND_OTHER
, UNITS_BYTES
,
430 amount
, nsCString(desc
), aData
);
438 Atomic
<size_t, Relaxed
>
439 SurfaceMemoryReporter::sSurfaceMemoryUsed
[size_t(gfxSurfaceType::Max
)];
441 NS_IMPL_ISUPPORTS(SurfaceMemoryReporter
, nsIMemoryReporter
)
443 void gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType
,
445 if (int(aType
) < 0 || aType
>= gfxSurfaceType::Max
) {
446 NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
450 static bool registered
= false;
452 RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
456 SurfaceMemoryReporter::AdjustUsedMemory(aType
, aBytes
);
459 void gfxASurface::RecordMemoryUsed(int32_t aBytes
) {
460 RecordMemoryUsedForSurfaceType(GetType(), aBytes
);
461 mBytesRecorded
+= aBytes
;
464 void gfxASurface::RecordMemoryFreed() {
465 if (mBytesRecorded
) {
466 RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded
);
471 size_t gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const {
472 // We don't measure mSurface because cairo doesn't allow it.
476 size_t gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
477 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
481 uint8_t gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat
) {
482 switch (aImageFormat
) {
483 case SurfaceFormat::A8R8G8B8_UINT32
:
485 case SurfaceFormat::X8R8G8B8_UINT32
:
487 case SurfaceFormat::R5G6B5_UINT16
:
489 case SurfaceFormat::A8
:
491 case SurfaceFormat::UNKNOWN
:
493 MOZ_ASSERT_UNREACHABLE("Not really sure what you want me to say here");
498 void gfxASurface::SetOpaqueRect(const gfxRect
& aRect
) {
499 if (aRect
.IsEmpty()) {
500 mOpaqueRect
= nullptr;
501 } else if (!!mOpaqueRect
) {
502 *mOpaqueRect
= aRect
;
504 mOpaqueRect
= MakeUnique
<gfxRect
>(aRect
);
508 /* static */ const gfxRect
& gfxASurface::GetEmptyOpaqueRect() {
509 static const gfxRect
empty(0, 0, 0, 0);
513 const IntSize
gfxASurface::GetSize() const { return IntSize(-1, -1); }
515 SurfaceFormat
gfxASurface::GetSurfaceFormat() const {
516 if (!mSurfaceValid
) {
517 return SurfaceFormat::UNKNOWN
;
519 return GfxFormatForCairoSurface(mSurface
);
522 already_AddRefed
<gfxImageSurface
> gfxASurface::GetAsImageSurface() {