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