зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1444430 - Add a ProfilerScreenshots class which can be used to submit DataSourceSurfaces to the profiler. r=jrmuizel
ProfilerScreenshots encodes these surfaces to JPG data URLs, and submits them as profiler markers. The encoding is done on a separate thread. MozReview-Commit-ID: 7CKDBqUsLny --HG-- extra : rebase_source : 45b608544b4ddf8502302cf974ca4e8b306de64e
This commit is contained in:
Родитель
de5088e49a
Коммит
2d93796161
|
@ -0,0 +1,142 @@
|
|||
/* -*- 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/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::layers;
|
||||
|
||||
ProfilerScreenshots::ProfilerScreenshots()
|
||||
: mMutex("ProfilerScreenshots::mMutex")
|
||||
, mLiveSurfaceCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
ProfilerScreenshots::~ProfilerScreenshots()
|
||||
{
|
||||
if (mThread) {
|
||||
mThread->Shutdown();
|
||||
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");
|
||||
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");
|
||||
ReturnSurface(backingSurface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int sourceThread = profiler_current_thread_id();
|
||||
uintptr_t windowIdentifier = aWindowIdentifier;
|
||||
IntSize originalSize = aOriginalSize;
|
||||
IntSize scaledSize = aScaledSize;
|
||||
TimeStamp timeStamp = aTimeStamp;
|
||||
|
||||
mThread->Dispatch(
|
||||
NS_NewRunnableFunction("ProfilerScreenshots::SubmitScreenshot",
|
||||
[this, 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, NS_LITERAL_CSTRING("image/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,
|
||||
"CompositorScreenshot",
|
||||
MakeUnique<ScreenshotPayload>(timeStamp, Move(dataURL),
|
||||
originalSize, windowIdentifier));
|
||||
}
|
||||
}
|
||||
|
||||
// Return backingSurface back to the surface pool.
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_layers_ProfilerScreenshots_h
|
||||
#define mozilla_layers_ProfilerScreenshots_h
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#include "mozilla/gfx/Point.h"
|
||||
|
||||
class nsIThread;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* Can be used to submit screenshots from the compositor to the profiler.
|
||||
* Screenshots have a fixed bounding size. The user of this class will usually
|
||||
* scale down the window contents first, ideally on the GPU, then read back the
|
||||
* small scaled down image into main memory, and then call SubmitScreenshot to
|
||||
* pass the data to the profiler.
|
||||
* This class encodes each screenshot to a JPEG data URL, on a separate thread.
|
||||
* This class manages that thread and recycles memory buffers.
|
||||
*/
|
||||
class ProfilerScreenshots final
|
||||
{
|
||||
public:
|
||||
ProfilerScreenshots();
|
||||
~ProfilerScreenshots();
|
||||
|
||||
/**
|
||||
* Returns whether the profiler is currently active and is running with the
|
||||
* "screenshots" feature enabled.
|
||||
*/
|
||||
static bool IsEnabled();
|
||||
|
||||
/**
|
||||
* Returns a fixed size that all screenshots should be resized to fit into.
|
||||
*/
|
||||
static gfx::IntSize ScreenshotSize() { return gfx::IntSize(350, 350); }
|
||||
|
||||
/**
|
||||
* The main functionality provided by this class.
|
||||
* This method will synchronously invoke the supplied aPopulateSurface
|
||||
* callback function with a DataSourceSurface in which the callback should
|
||||
* store the pixel data. This surface may be larger than aScaledSize, but
|
||||
* only the data in the rectangle (0, 0, aScaledSize.width, aScaledSize.height)
|
||||
* will be read.
|
||||
* @param aWindowIdentifier A pointer-sized integer that can be used to match
|
||||
* up multiple screenshots from the same window.
|
||||
* @param aOriginalSize The unscaled size of the snapshotted window.
|
||||
* @param aScaledSize The scaled size, aScaled <= ScreenshotSize(), which the
|
||||
* snapshot has been resized to.
|
||||
* @param aTimeStamp The time at which the snapshot was taken. In
|
||||
* asynchronous readback implementations, this is the time at which the
|
||||
* readback / copy command was put into the command stream to the GPU, not
|
||||
* the time at which the readback data was mapped into main memory.
|
||||
* @param aPopulateSurface A callback that the caller needs to implement,
|
||||
* which needs to copy the screenshot pixel data into the surface that's
|
||||
* supplied to the callback. Called zero or one times, synchronously.
|
||||
*/
|
||||
void SubmitScreenshot(uintptr_t aWindowIdentifier, const gfx::IntSize& aOriginalSize,
|
||||
const gfx::IntSize& aScaledSize, const TimeStamp& aTimeStamp,
|
||||
const std::function<bool(gfx::DataSourceSurface*)>& aPopulateSurface);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Recycle a surface from mAvailableSurfaces or create a new one if all
|
||||
* surfaces are currently in use, up to some maximum limit.
|
||||
* Returns null if the limit is reached.
|
||||
* Can be called on any thread.
|
||||
*/
|
||||
already_AddRefed<DataSourceSurface> TakeNextSurface();
|
||||
|
||||
/**
|
||||
* Return aSurface back into the mAvailableSurfaces pool. Can be called on
|
||||
* any thread.
|
||||
*/
|
||||
void ReturnSurface(DataSourceSurface* aSurface);
|
||||
|
||||
// The thread on which encoding happens.
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
// An array of surfaces ready to be recycled. Can be accessed from multiple
|
||||
// threads, protected by mMutex.
|
||||
nsTArray<RefPtr<DataSourceSurface>> mAvailableSurfaces;
|
||||
// Protects mAvailableSurfaces.
|
||||
Mutex mMutex;
|
||||
// The total number of surfaces created. If encoding is fast enough to happen
|
||||
// entirely in the time between two calls to SubmitScreenshot, this should
|
||||
// never exceed 1.
|
||||
uint32_t mLiveSurfaceCount;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_ProfilerScreenshots_h
|
|
@ -221,6 +221,7 @@ EXPORTS.mozilla.layers += [
|
|||
'opengl/TextureHostOGL.h',
|
||||
'PaintThread.h',
|
||||
'PersistentBufferProvider.h',
|
||||
'ProfilerScreenshots.h',
|
||||
'RenderTrace.h',
|
||||
'RotatedBuffer.h',
|
||||
'ShareableCanvasRenderer.h',
|
||||
|
@ -457,6 +458,7 @@ UNIFIED_SOURCES += [
|
|||
'opengl/TextureClientOGL.cpp',
|
||||
'opengl/TextureHostOGL.cpp',
|
||||
'PaintThread.cpp',
|
||||
'ProfilerScreenshots.cpp',
|
||||
'ReadbackProcessor.cpp',
|
||||
'RenderTrace.cpp',
|
||||
'RotatedBuffer.cpp',
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* 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 <inttypes.h>
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#include "ProfilerBacktrace.h"
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
|
@ -128,6 +130,20 @@ VsyncMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
|
|||
aWriter.StringProperty("category", "VsyncTimestamp");
|
||||
}
|
||||
|
||||
void
|
||||
ScreenshotPayload::StreamPayload(SpliceableJSONWriter& aWriter,
|
||||
const TimeStamp& aProcessStartTime,
|
||||
UniqueStacks& aUniqueStacks)
|
||||
{
|
||||
aUniqueStacks.mUniqueStrings->WriteProperty(aWriter, "url", mScreenshotDataURL.get());
|
||||
|
||||
char hexWindowID[32];
|
||||
SprintfLiteral(hexWindowID, "0x%" PRIXPTR, mWindowIdentifier);
|
||||
aWriter.StringProperty("windowID", hexWindowID);
|
||||
aWriter.DoubleProperty("windowWidth", mWindowSize.width);
|
||||
aWriter.DoubleProperty("windowHeight", mWindowSize.height);
|
||||
}
|
||||
|
||||
void
|
||||
GCSliceMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
|
||||
const TimeStamp& aProcessStartTime,
|
||||
|
|
|
@ -210,6 +210,27 @@ private:
|
|||
mozilla::TimeStamp mVsyncTimestamp;
|
||||
};
|
||||
|
||||
class ScreenshotPayload : public ProfilerMarkerPayload
|
||||
{
|
||||
public:
|
||||
explicit ScreenshotPayload(mozilla::TimeStamp aTimeStamp,
|
||||
nsCString&& aScreenshotDataURL,
|
||||
const mozilla::gfx::IntSize& aWindowSize,
|
||||
uintptr_t aWindowIdentifier)
|
||||
: ProfilerMarkerPayload(aTimeStamp, mozilla::TimeStamp())
|
||||
, mScreenshotDataURL(mozilla::Move(aScreenshotDataURL))
|
||||
, mWindowSize(aWindowSize)
|
||||
, mWindowIdentifier(aWindowIdentifier)
|
||||
{}
|
||||
|
||||
DECL_STREAM_PAYLOAD
|
||||
|
||||
private:
|
||||
nsCString mScreenshotDataURL;
|
||||
mozilla::gfx::IntSize mWindowSize;
|
||||
uintptr_t mWindowIdentifier;
|
||||
};
|
||||
|
||||
class GCSliceMarkerPayload : public ProfilerMarkerPayload
|
||||
{
|
||||
public:
|
||||
|
|
Загрузка…
Ссылка в новой задаче