tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 96b4eab236cfeb217bf41ba609cb47738fe7a477
parent 86a403a0d1ffba84084bac978873d36fd47a020b
Author: Lee Salzman <lsalzman@mozilla.com>
Date:   Tue, 28 Oct 2025 16:54:53 +0000

Bug 1996856 - Avoid DrawTarget::ClearRect when BufferTexture is already clear. r=aosmond

Since BufferTextures are often initialized to a clear value, it is redundant to subsequently
do an initial ClearRect on a subsequent BorrowDrawTarget.

This tracks whether BufferTexture creation initialized the memory to clear and passes this
information to BorrowDrawTarget so that ClearRect can be avoided.

Differential Revision: https://phabricator.services.mozilla.com/D270358

Diffstat:
Mgfx/2d/2D.h | 3++-
Mgfx/2d/DrawTargetSkia.cpp | 16++++++++++++++--
Mgfx/2d/DrawTargetSkia.h | 6++++--
Mgfx/2d/Factory.cpp | 6++++--
Mgfx/layers/BufferTexture.cpp | 31++++++++++++++++++++-----------
Mgfx/layers/BufferTexture.h | 9++++++---
Mgfx/thebes/gfxPlatform.cpp | 4++--
Mgfx/thebes/gfxPlatform.h | 3++-
8 files changed, 54 insertions(+), 24 deletions(-)

diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h @@ -2206,7 +2206,8 @@ class GFX2D_API Factory { static already_AddRefed<DrawTarget> CreateDrawTargetForData( BackendType aBackend, unsigned char* aData, const IntSize& aSize, - int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false); + int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false, + bool aIsClear = false); #ifdef XP_DARWIN static already_AddRefed<ScaledFont> CreateScaledFontForMacFont( diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp @@ -1847,6 +1847,7 @@ bool DrawTargetSkia::Init(const IntSize& aSize, SurfaceFormat aFormat) { if (info.isOpaque()) { mCanvas->clear(SK_ColorBLACK); } + mIsClear = true; return true; } @@ -1861,6 +1862,7 @@ bool DrawTargetSkia::Init(SkCanvas* aCanvas) { SkColor clearColor = imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT; mCanvas->clear(clearColor); + mIsClear = true; } SkISize size = mCanvas->getBaseLayerSize(); @@ -1874,7 +1876,7 @@ bool DrawTargetSkia::Init(SkCanvas* aCanvas) { bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat, - bool aUninitialized) { + bool aUninitialized, bool aIsClear) { MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) || aUninitialized || VerifyRGBXFormat(aData, aSize, aStride, aFormat)); @@ -1894,6 +1896,7 @@ bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize, mFormat = aFormat; mCanvas = mSurface->getCanvas(); SetPermitSubpixelAA(IsOpaque(mFormat)); + mIsClear = aIsClear; return true; } @@ -1958,6 +1961,10 @@ already_AddRefed<PathBuilder> DrawTargetSkia::CreatePathBuilder( } void DrawTargetSkia::ClearRect(const Rect& aRect) { + if (mIsClear) { + return; + } + MarkChanged(); SkPaint paint; paint.setAntiAlias(true); @@ -2142,7 +2149,7 @@ already_AddRefed<FilterNode> DrawTargetSkia::CreateFilter(FilterType aType) { return FilterNodeSoftware::Create(aType); } -void DrawTargetSkia::MarkChanged() { +void DrawTargetSkia::DetachAllSnapshots() { // I'm not entirely certain whether this lock is needed, as multiple threads // should never modify the DrawTarget at the same time anyway, but this seems // like the safest. @@ -2164,4 +2171,9 @@ void DrawTargetSkia::MarkChanged() { } } +void DrawTargetSkia::MarkChanged() { + DetachAllSnapshots(); + mIsClear = false; +} + } // namespace mozilla::gfx diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h @@ -140,11 +140,12 @@ class DrawTargetSkia : public DrawTarget { virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override; virtual void SetTransform(const Matrix& aTransform) override; virtual void* GetNativeSurface(NativeSurfaceType aType) override; - virtual void DetachAllSnapshots() override { MarkChanged(); } + virtual void DetachAllSnapshots() override; bool Init(const IntSize& aSize, SurfaceFormat aFormat); bool Init(unsigned char* aData, const IntSize& aSize, int32_t aStride, - SurfaceFormat aFormat, bool aUninitialized = false); + SurfaceFormat aFormat, bool aUninitialized = false, + bool aIsClear = false); bool Init(SkCanvas* aCanvas); bool Init(RefPtr<DataSourceSurface>&& aSurface); @@ -196,6 +197,7 @@ class DrawTargetSkia : public DrawTarget { RefPtr<DataSourceSurface> mBackingSurface; RefPtr<SourceSurfaceSkia> mSnapshot; Mutex mSnapshotLock MOZ_UNANNOTATED; + bool mIsClear = false; #ifdef XP_DARWIN friend class BorrowedCGContext; diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp @@ -432,7 +432,8 @@ already_AddRefed<DrawTarget> Factory::CreateRecordingDrawTarget( already_AddRefed<DrawTarget> Factory::CreateDrawTargetForData( BackendType aBackend, unsigned char* aData, const IntSize& aSize, - int32_t aStride, SurfaceFormat aFormat, bool aUninitialized) { + int32_t aStride, SurfaceFormat aFormat, bool aUninitialized, + bool aIsClear) { MOZ_ASSERT(aData); if (!AllowedSurfaceSize(aSize)) { gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) @@ -446,7 +447,8 @@ already_AddRefed<DrawTarget> Factory::CreateDrawTargetForData( case BackendType::SKIA: { RefPtr<DrawTargetSkia> newTarget; newTarget = new DrawTargetSkia(); - if (newTarget->Init(aData, aSize, aStride, aFormat, aUninitialized)) { + if (newTarget->Init(aData, aSize, aStride, aFormat, aUninitialized, + aIsClear)) { retVal = newTarget; } break; diff --git a/gfx/layers/BufferTexture.cpp b/gfx/layers/BufferTexture.cpp @@ -49,8 +49,9 @@ class MemoryTextureData : public BufferTextureData { MemoryTextureData(const BufferDescriptor& aDesc, gfx::BackendType aMoz2DBackend, uint8_t* aBuffer, - size_t aBufferSize, bool aAutoDeallocate = false) - : BufferTextureData(aDesc, aMoz2DBackend), + size_t aBufferSize, bool aAutoDeallocate = false, + bool aIsClear = false) + : BufferTextureData(aDesc, aMoz2DBackend, aIsClear), mBuffer(aBuffer), mBufferSize(aBufferSize), mAutoDeallocate(aAutoDeallocate) { @@ -96,8 +97,9 @@ class ShmemTextureData : public BufferTextureData { virtual void Deallocate(LayersIPCChannel* aAllocator) override; ShmemTextureData(const BufferDescriptor& aDesc, - gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem) - : BufferTextureData(aDesc, aMoz2DBackend), mShmem(aShmem) { + gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem, + bool aIsClear = false) + : BufferTextureData(aDesc, aMoz2DBackend, aIsClear), mShmem(aShmem) { MOZ_ASSERT(mShmem.Size<uint8_t>()); } @@ -144,7 +146,9 @@ BufferTextureData* BufferTextureData::CreateInternal( return nullptr; } - return new ShmemTextureData(aDesc, aMoz2DBackend, shm); + bool isClear = aDesc.type() == BufferDescriptor::TRGBDescriptor && + !IsOpaque(aDesc.get_RGBDescriptor().format()); + return new ShmemTextureData(aDesc, aMoz2DBackend, shm, isClear); } } @@ -249,14 +253,15 @@ already_AddRefed<gfx::DrawTarget> BufferTextureData::BorrowDrawTarget() { uint32_t stride = ImageDataSerializer::GetRGBStride(rgb); RefPtr<gfx::DrawTarget> dt; if (gfx::Factory::DoesBackendSupportDataDrawtarget(mMoz2DBackend)) { - dt = gfx::Factory::CreateDrawTargetForData( - mMoz2DBackend, GetBuffer(), rgb.size(), stride, rgb.format(), true); + dt = gfx::Factory::CreateDrawTargetForData(mMoz2DBackend, GetBuffer(), + rgb.size(), stride, rgb.format(), + true, mIsClear); } if (!dt) { // Fall back to supported platform backend. Note that mMoz2DBackend // does not match the draw target type. dt = gfxPlatform::CreateDrawTargetForData(GetBuffer(), rgb.size(), stride, - rgb.format(), true); + rgb.format(), true, mIsClear); } if (!dt) { @@ -279,6 +284,7 @@ bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) { aData.format = GetFormat(); aData.stride = ImageDataSerializer::ComputeRGBStride(aData.format, size.width); + mIsClear = false; return true; } @@ -377,6 +383,8 @@ bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) { srcSurf->Unmap(); surface->Unmap(); + mIsClear = false; + return true; } @@ -447,8 +455,9 @@ MemoryTextureData* MemoryTextureData::Create(gfx::IntSize aSize, // Remote textures are not managed by a texture client, so we need to ensure // that memory is freed when the owning MemoryTextureData goes away. bool autoDeallocate = !!(aFlags & TextureFlags::REMOTE_TEXTURE); + bool isClear = (aAllocFlags & ALLOC_CLEAR_BUFFER) != 0; return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize, - autoDeallocate); + autoDeallocate, isClear); } void MemoryTextureData::Deallocate(LayersIPCChannel*) { @@ -515,8 +524,8 @@ ShmemTextureData* ShmemTextureData::Create(gfx::IntSize aSize, } BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat); - - return new ShmemTextureData(descriptor, aMoz2DBackend, shm); + bool isClear = (aAllocFlags & ALLOC_CLEAR_BUFFER) || !IsOpaque(aFormat); + return new ShmemTextureData(descriptor, aMoz2DBackend, shm, isClear); } TextureData* ShmemTextureData::CreateSimilar( diff --git a/gfx/layers/BufferTexture.h b/gfx/layers/BufferTexture.h @@ -38,7 +38,7 @@ class BufferTextureData : public TextureData { bool Lock(OpenMode aMode) override { return true; } - void Unlock() override {} + void Unlock() override { mIsClear = false; } void FillInfo(TextureData::Info& aInfo) const override; @@ -93,13 +93,16 @@ class BufferTextureData : public TextureData { virtual uint8_t* GetBuffer() = 0; BufferTextureData(const BufferDescriptor& aDescriptor, - gfx::BackendType aMoz2DBackend) - : mDescriptor(aDescriptor), mMoz2DBackend(aMoz2DBackend) {} + gfx::BackendType aMoz2DBackend, bool aIsClear = false) + : mDescriptor(aDescriptor), + mMoz2DBackend(aMoz2DBackend), + mIsClear(aIsClear) {} ~BufferTextureData() override = default; BufferDescriptor mDescriptor; gfx::BackendType mMoz2DBackend; + bool mIsClear = false; }; template <typename ShmemAllocator> diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp @@ -1771,7 +1771,7 @@ already_AddRefed<DrawTarget> gfxPlatform::CreateSimilarSoftwareDrawTarget( /* static */ already_AddRefed<DrawTarget> gfxPlatform::CreateDrawTargetForData( unsigned char* aData, const IntSize& aSize, int32_t aStride, - SurfaceFormat aFormat, bool aUninitialized) { + SurfaceFormat aFormat, bool aUninitialized, bool aIsClear) { BackendType backendType = gfxVars::ContentBackend(); NS_ASSERTION(backendType != BackendType::NONE, "No backend."); @@ -1780,7 +1780,7 @@ already_AddRefed<DrawTarget> gfxPlatform::CreateDrawTargetForData( } RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData( - backendType, aData, aSize, aStride, aFormat, aUninitialized); + backendType, aData, aSize, aStride, aFormat, aUninitialized, aIsClear); return dt.forget(); } diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h @@ -284,7 +284,8 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener { static already_AddRefed<DrawTarget> CreateDrawTargetForData( unsigned char* aData, const mozilla::gfx::IntSize& aSize, int32_t aStride, - mozilla::gfx::SurfaceFormat aFormat, bool aUninitialized = false); + mozilla::gfx::SurfaceFormat aFormat, bool aUninitialized = false, + bool aIsClear = false); /** * Returns true if we should use Azure to render content with aTarget. For