Bug 1298345 - Refactor CanvasRenderingContext2D's texture allocation code. r=Bas

This commit is contained in:
Nicolas Silva 2016-09-01 15:51:09 +02:00
Родитель d056325bfe
Коммит 47e7bc67f9
2 изменённых файлов: 231 добавлений и 149 удалений

Просмотреть файл

@ -1546,8 +1546,7 @@ CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
{
if (AlreadyShutDown()) {
gfxCriticalError() << "Attempt to render into a Canvas2d after shutdown.";
EnsureErrorTarget();
mTarget = sErrorTarget;
SetErrorState();
return aRenderingMode;
}
@ -1560,6 +1559,15 @@ CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
return mRenderingMode;
}
// Check that the dimensions are sane
if (mWidth > gfxPrefs::MaxCanvasSize() ||
mHeight > gfxPrefs::MaxCanvasSize() ||
mWidth < 0 || mHeight < 0) {
SetErrorState();
return aRenderingMode;
}
// If the next drawing command covers the entire canvas, we can skip copying
// from the previous frame and/or clearing the canvas.
gfx::Rect canvasRect(0, 0, mWidth, mHeight);
@ -1582,13 +1590,10 @@ CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
ScheduleStableStateCallback();
// we'll do a few extra things at the end of this method if we changed the
// buffer provider.
RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
IntRect persistedRect = canDiscardContent ? IntRect()
: IntRect(0, 0, mWidth, mHeight);
if (mBufferProvider && mode == mRenderingMode) {
auto persistedRect = canDiscardContent ? IntRect()
: IntRect(0, 0, mWidth, mHeight);
mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
if (mTarget && !mBufferProvider->PreservesDrawingState()) {
@ -1600,112 +1605,211 @@ CanvasRenderingContext2D::EnsureTarget(const gfx::Rect* aCoveredRect,
}
}
mIsSkiaGL = false;
RefPtr<DrawTarget> newTarget;
RefPtr<PersistentBufferProvider> newProvider;
// Check that the dimensions are sane
IntSize size(mWidth, mHeight);
if (!mTarget &&
size.width <= gfxPrefs::MaxCanvasSize() &&
size.height <= gfxPrefs::MaxCanvasSize() &&
size.width >= 0 && size.height >= 0) {
SurfaceFormat format = GetSurfaceFormat();
nsIDocument* ownerDoc = nullptr;
if (mCanvasElement) {
ownerDoc = mCanvasElement->OwnerDoc();
}
RefPtr<LayerManager> layerManager = nullptr;
if (ownerDoc) {
layerManager =
nsContentUtils::PersistentLayerManagerForDocument(ownerDoc);
}
if (layerManager) {
if (mode == RenderingMode::OpenGLBackendMode &&
gfxPlatform::GetPlatform()->UseAcceleratedCanvas() &&
CheckSizeForSkiaGL(size)) {
DemoteOldestContextIfNecessary();
mBufferProvider = nullptr;
#if USE_SKIA_GPU
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
if (glue && glue->GetGrContext() && glue->GetGLContext()) {
mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
if (mTarget) {
AddDemotableContext(this);
mBufferProvider = new PersistentBufferProviderBasic(mTarget);
mIsSkiaGL = true;
} else {
gfxCriticalNote << "Failed to create a SkiaGL DrawTarget, falling back to software " << size << ", " << format;
mode = RenderingMode::SoftwareBackendMode;
}
}
#endif
}
if (!mBufferProvider) {
mTarget = nullptr;
mBufferProvider = layerManager->CreatePersistentBufferProvider(size, format);
}
}
if (mBufferProvider) {
mTarget = mBufferProvider->BorrowDrawTarget(IntRect(IntPoint(), IntSize(mWidth, mHeight)));
} else if (!mTarget) {
mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
mode = RenderingMode::SoftwareBackendMode;
}
if (mode == RenderingMode::OpenGLBackendMode &&
!TrySkiaGLTarget(newTarget, newProvider)) {
// Fall back to software.
mode = RenderingMode::SoftwareBackendMode;
}
if (mTarget) {
// We changed the buffer provider.
// XXX - It would make more sense to track the allocation in
// PeristentBufferProvider, rather than here.
if (mBufferProvider != oldBufferProvider) {
static bool registered = false;
if (!registered) {
registered = true;
RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
}
gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
JSContext* context = nsContentUtils::GetCurrentJSContext();
if (context) {
JS_updateMallocCounter(context, mWidth * mHeight * 4);
}
mTarget->ClearRect(canvasRect);
// Force a full layer transaction since we didn't have a layer before
// and now we might need one.
if (mCanvasElement) {
mCanvasElement->InvalidateCanvas();
}
// Calling Redraw() tells our invalidation machinery that the entire
// canvas is already invalid, which can speed up future drawing.
Redraw();
}
if (mBufferProvider != oldBufferProvider || !mBufferProvider ||
!mBufferProvider->PreservesDrawingState()) {
RestoreClipsAndTransformToTarget();
}
} else {
EnsureErrorTarget();
mTarget = sErrorTarget;
mBufferProvider = nullptr;
if (mode == RenderingMode::SoftwareBackendMode &&
!TrySharedTarget(newTarget, newProvider) &&
!TryBasicTarget(newTarget, newProvider)) {
SetErrorState();
return mode;
}
// Drop a note in the debug builds if we ever use accelerated Skia canvas.
if (mIsSkiaGL && mTarget && mTarget->GetType() == DrawTargetType::HARDWARE_RASTER) {
gfxWarningOnce() << "Using SkiaGL canvas.";
MOZ_ASSERT(newTarget);
MOZ_ASSERT(newProvider);
mTarget = newTarget.forget();
mBufferProvider = newProvider.forget();
RegisterAllocation();
// Skia expects the unused X channel to contains 0 even for opaque operations
// so we can't skip clearing in that case, even if we are going to cover the
// entire canvas in the next drawing operation.
if (!canDiscardContent || mTarget->GetBackendType() == gfx::BackendType::SKIA) {
mTarget->ClearRect(canvasRect);
}
RestoreClipsAndTransformToTarget();
// Force a full layer transaction since we didn't have a layer before
// and now we might need one.
if (mCanvasElement) {
mCanvasElement->InvalidateCanvas();
}
// Calling Redraw() tells our invalidation machinery that the entire
// canvas is already invalid, which can speed up future drawing.
Redraw();
return mode;
}
void
CanvasRenderingContext2D::SetInitialState()
{
// Set up the initial canvas defaults
mPathBuilder = nullptr;
mPath = nullptr;
mDSPathBuilder = nullptr;
mStyleStack.Clear();
ContextState *state = mStyleStack.AppendElement();
state->globalAlpha = 1.0;
state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
state->shadowColor = NS_RGBA(0,0,0,0);
}
void
CanvasRenderingContext2D::SetErrorState()
{
EnsureErrorTarget();
if (mTarget && mTarget != sErrorTarget) {
gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
}
mTarget = sErrorTarget;
mBufferProvider = nullptr;
// clear transforms, clips, etc.
SetInitialState();
}
void
CanvasRenderingContext2D::RegisterAllocation()
{
// XXX - It would make more sense to track the allocation in
// PeristentBufferProvider, rather than here.
static bool registered = false;
if (!registered) {
registered = true;
RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
}
gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
JSContext* context = nsContentUtils::GetCurrentJSContext();
if (context) {
JS_updateMallocCounter(context, mWidth * mHeight * 4);
}
}
static already_AddRefed<LayerManager>
LayerManagerFromCanvasElement(nsINode* aCanvasElement)
{
if (!aCanvasElement || !aCanvasElement->OwnerDoc()) {
return nullptr;
}
return nsContentUtils::PersistentLayerManagerForDocument(aCanvasElement->OwnerDoc());
}
bool
CanvasRenderingContext2D::TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider)
{
aOutDT = nullptr;
aOutProvider = nullptr;
mIsSkiaGL = false;
IntSize size(mWidth, mHeight);
if (!gfxPlatform::GetPlatform()->UseAcceleratedCanvas() ||
!CheckSizeForSkiaGL(size)) {
return false;
}
RefPtr<LayerManager> layerManager = LayerManagerFromCanvasElement(mCanvasElement);
if (!layerManager) {
return false;
}
DemoteOldestContextIfNecessary();
mBufferProvider = nullptr;
#if USE_SKIA_GPU
SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
if (!glue || !glue->GetGrContext() || !glue->GetGLContext()) {
return false;
}
SurfaceFormat format = GetSurfaceFormat();
aOutDT = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(),
size, format);
if (!aOutDT) {
return false;
gfxCriticalNote << "Failed to create a SkiaGL DrawTarget, falling back to software\n";
}
MOZ_ASSERT(aOutDT->GetType() == DrawTargetType::HARDWARE_RASTER);
AddDemotableContext(this);
aOutProvider = new PersistentBufferProviderBasic(aOutDT);
mIsSkiaGL = true;
// Drop a note in the debug builds if we ever use accelerated Skia canvas.
gfxWarningOnce() << "Using SkiaGL canvas.";
#endif
// could still be null if USE_SKIA_GPU is not #defined.
return !!aOutDT;
}
bool
CanvasRenderingContext2D::TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider)
{
aOutDT = nullptr;
aOutProvider = nullptr;
if (!mCanvasElement || !mCanvasElement->OwnerDoc()) {
return false;
}
RefPtr<LayerManager> layerManager = LayerManagerFromCanvasElement(mCanvasElement);
if (!layerManager) {
return false;
}
aOutProvider = layerManager->CreatePersistentBufferProvider(GetSize(), GetSurfaceFormat());
if (!aOutProvider) {
return false;
}
// We can pass an empty persisted rect since we just created the buffer
// provider (nothing to restore).
aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
MOZ_ASSERT(aOutDT);
return !!aOutDT;
}
bool
CanvasRenderingContext2D::TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider)
{
aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(GetSize(),
GetSurfaceFormat());
if (!aOutDT) {
return false;
}
aOutProvider = new PersistentBufferProviderBasic(aOutDT);
return true;
}
int32_t
CanvasRenderingContext2D::GetWidth() const
{
@ -1763,18 +1867,7 @@ CanvasRenderingContext2D::ClearTarget(bool aRetainBuffer)
mResetLayer = true;
// set up the initial canvas defaults
mStyleStack.Clear();
mPathBuilder = nullptr;
mPath = nullptr;
mDSPathBuilder = nullptr;
ContextState *state = mStyleStack.AppendElement();
state->globalAlpha = 1.0;
state->colorStyles[Style::FILL] = NS_RGB(0,0,0);
state->colorStyles[Style::STROKE] = NS_RGB(0,0,0);
state->shadowColor = NS_RGBA(0,0,0,0);
SetInitialState();
// For vertical writing-mode, unless text-orientation is sideways,
// we'll modify the initial value of textBaseline to 'middle'.
@ -1789,7 +1882,7 @@ CanvasRenderingContext2D::ClearTarget(bool aRetainBuffer)
if (canvasStyle) {
WritingMode wm(canvasStyle);
if (wm.IsVertical() && !wm.IsSideways()) {
state->textBaseline = TextBaseline::MIDDLE;
CurrentState().textBaseline = TextBaseline::MIDDLE;
}
}
}
@ -1920,7 +2013,7 @@ CanvasRenderingContext2D::GetImageBuffer(int32_t* aFormat)
if (snapshot) {
RefPtr<DataSourceSurface> data = snapshot->GetDataSurface();
if (data && data->GetSize() == IntSize(mWidth, mHeight)) {
if (data && data->GetSize() == GetSize()) {
*aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
ret = SurfaceToPackedBGRA(data);
}
@ -2762,7 +2855,7 @@ CanvasRenderingContext2D::UpdateFilter()
CurrentState().filter =
nsFilterInstance::GetFilterDescription(mCanvasElement,
CurrentState().filterChain,
CanvasUserSpaceMetrics(IntSize(mWidth, mHeight),
CanvasUserSpaceMetrics(GetSize(),
CurrentState().fontFont,
CurrentState().fontLanguage,
CurrentState().fontExplicitLanguage,
@ -5798,30 +5891,6 @@ void CanvasRenderingContext2D::RemoveDrawObserver()
}
}
PersistentBufferProvider*
CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
{
if (AlreadyShutDown()) {
return nullptr;
}
if (mBufferProvider) {
return mBufferProvider;
}
if (mTarget) {
mBufferProvider = new PersistentBufferProviderBasic(mTarget);
return mBufferProvider;
}
if (aManager && !mIsSkiaGL) {
mBufferProvider = aManager->CreatePersistentBufferProvider(gfx::IntSize(mWidth, mHeight),
GetSurfaceFormat());
}
return mBufferProvider;
}
already_AddRefed<Layer>
CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
@ -5869,8 +5938,7 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
}
}
PersistentBufferProvider *provider = GetBufferProvider(aManager);
data.mBufferProvider = provider;
data.mBufferProvider = mBufferProvider;
if (userData &&
userData->IsForContext(this) &&
@ -5906,7 +5974,7 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
canvasLayer->SetUserData(&g2DContextLayerUserData, userData);
CanvasLayer::Data data;
data.mSize = nsIntSize(mWidth, mHeight);
data.mSize = GetSize();
data.mHasAlpha = !mOpaque;
canvasLayer->SetPreTransactionCallback(
@ -5923,8 +5991,7 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
}
}
PersistentBufferProvider *provider = GetBufferProvider(aManager);
data.mBufferProvider = provider;
data.mBufferProvider = mBufferProvider;
canvasLayer->Initialize(data);
uint32_t flags = mOpaque ? Layer::CONTENT_OPAQUE : 0;
@ -5961,7 +6028,7 @@ CanvasRenderingContext2D::IsContextCleanForFrameCapture()
bool
CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager* aManager)
{
return !aManager->CanUseCanvasLayerForSize(IntSize(mWidth, mHeight));
return !aManager->CanUseCanvasLayerForSize(GetSize());
}
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPath, AddRef)

Просмотреть файл

@ -429,6 +429,7 @@ public:
virtual int32_t GetWidth() const override;
virtual int32_t GetHeight() const override;
gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
// nsICanvasRenderingContextInternal
/**
@ -463,7 +464,6 @@ public:
NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
bool GetIsOpaque() override { return mOpaque; }
NS_IMETHOD Reset() override;
mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager);
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager,
@ -645,6 +645,21 @@ protected:
void RestoreClipsAndTransformToTarget();
bool TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider);
bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider);
bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
RefPtr<layers::PersistentBufferProvider>& aOutProvider);
void RegisterAllocation();
void SetInitialState();
void SetErrorState();
/**
* This method is run at the end of the event-loop spin where
* ScheduleStableStateCallback was called.