DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/layers/ProfilerScreenshots.h"

#include "mozilla/SystemGroup.h"
#include "mozilla/TimeStamp.h"

#include "GeckoProfiler.h"
#include "gfxUtils.h"
#include "nsThreadUtils.h"
#ifdef MOZ_GECKO_PROFILER
#  include "ProfilerMarkerPayload.h"
#endif

using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;

ProfilerScreenshots::ProfilerScreenshots()
    : mMutex("ProfilerScreenshots::mMutex"), mLiveSurfaceCount(0) {}

ProfilerScreenshots::~ProfilerScreenshots() {
  if (mThread) {
    // Shut down mThread. Do the actual shutdown on the main thread, because it
    // has to happen on an XPCOM thread, and ~ProfilerScreenshots() may not be
    // running on an XPCOM thread - it usually runs on the Compositor thread
    // which is a chromium thread.
    SystemGroup::Dispatch(
        TaskCategory::Other,
        NewRunnableMethod("ProfilerScreenshots::~ProfilerScreenshots", mThread,
                          &nsIThread::AsyncShutdown));
    mThread = nullptr;
  }
}

/* static */
bool ProfilerScreenshots::IsEnabled() {
#ifdef MOZ_GECKO_PROFILER
  return profiler_feature_active(ProfilerFeature::Screenshots);
#else
  return false;
#endif
}

void ProfilerScreenshots::SubmitScreenshot(
    uintptr_t aWindowIdentifier, const gfx::IntSize& aOriginalSize,
    const IntSize& aScaledSize, const TimeStamp& aTimeStamp,
    const std::function<bool(DataSourceSurface*)>& aPopulateSurface) {
#ifdef MOZ_GECKO_PROFILER
  RefPtr<DataSourceSurface> backingSurface = TakeNextSurface();
  if (!backingSurface) {
    return;
  }

  MOZ_RELEASE_ASSERT(aScaledSize <= backingSurface->GetSize());

  bool succeeded = aPopulateSurface(backingSurface);

  if (!succeeded) {
    PROFILER_ADD_MARKER(
        "NoCompositorScreenshot because aPopulateSurface callback failed",
        GRAPHICS);
    ReturnSurface(backingSurface);
    return;
  }

  if (!mThread) {
    nsresult rv = NS_NewNamedThread("ProfScreenshot", getter_AddRefs(mThread));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      PROFILER_ADD_MARKER(
          "NoCompositorScreenshot because ProfilerScreenshots thread creation "
          "failed",
          DOM);
      ReturnSurface(backingSurface);
      return;
    }
  }

  int sourceThread = profiler_current_thread_id();
  uintptr_t windowIdentifier = aWindowIdentifier;
  IntSize originalSize = aOriginalSize;
  IntSize scaledSize = aScaledSize;
  TimeStamp timeStamp = aTimeStamp;

  RefPtr<ProfilerScreenshots> self = this;

  mThread->Dispatch(NS_NewRunnableFunction(
      "ProfilerScreenshots::SubmitScreenshot",
      [self{std::move(self)}, backingSurface, sourceThread, windowIdentifier,
       originalSize, scaledSize, timeStamp]() {
        // Create a new surface that wraps backingSurface's data but has the
        // correct size.
        {
          DataSourceSurface::ScopedMap scopedMap(backingSurface,
                                                 DataSourceSurface::READ);
          RefPtr<DataSourceSurface> surf =
              Factory::CreateWrappingDataSourceSurface(
                  scopedMap.GetData(), scopedMap.GetStride(), scaledSize,
                  SurfaceFormat::B8G8R8A8);

          // Encode surf to a JPEG data URL.
          nsCString dataURL;
          nsresult rv = gfxUtils::EncodeSourceSurface(
              surf, ImageType::JPEG, NS_LITERAL_STRING("quality=85"),
              gfxUtils::eDataURIEncode, nullptr, &dataURL);
          if (NS_SUCCEEDED(rv)) {
            // Add a marker with the data URL.
            profiler_add_marker_for_thread(
                sourceThread, JS::ProfilingCategoryPair::GRAPHICS,
                "CompositorScreenshot",
                MakeUnique<ScreenshotPayload>(timeStamp, std::move(dataURL),
                                              originalSize, windowIdentifier));
          }
        }

        // Return backingSurface back to the surface pool.
        self->ReturnSurface(backingSurface);
      }));
#endif
}

already_AddRefed<DataSourceSurface> ProfilerScreenshots::TakeNextSurface() {
  MutexAutoLock mon(mMutex);
  if (!mAvailableSurfaces.IsEmpty()) {
    RefPtr<DataSourceSurface> surf = mAvailableSurfaces[0];
    mAvailableSurfaces.RemoveElementAt(0);
    return surf.forget();
  }
  if (mLiveSurfaceCount >= 8) {
    NS_WARNING(
        "already 8 surfaces in flight, skipping capture for this composite");
    return nullptr;
  }
  mLiveSurfaceCount++;
  return Factory::CreateDataSourceSurface(ScreenshotSize(),
                                          SurfaceFormat::B8G8R8A8);
}

void ProfilerScreenshots::ReturnSurface(DataSourceSurface* aSurface) {
  MutexAutoLock mon(this->mMutex);
  mAvailableSurfaces.AppendElement(aSurface);
}