Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h
Warning:line 80, column 7
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name DrawTargetWebgl.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/canvas -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/canvas -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/dom/canvas -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dom/canvas -I /root/firefox-clang/js/xpconnect/wrappers -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/dom/base -I /root/firefox-clang/dom/html -I /root/firefox-clang/dom/svg -I /root/firefox-clang/dom/workers -I /root/firefox-clang/dom/xul -I /root/firefox-clang/gfx/angle/checkout/include -I /root/firefox-clang/gfx/cairo/cairo/src -I /root/firefox-clang/gfx/gl -I /root/firefox-clang/image -I /root/firefox-clang/js/xpconnect/src -I /root/firefox-clang/layout/generic -I /root/firefox-clang/layout/style -I /root/firefox-clang/layout/xul -I /root/firefox-clang/media/libyuv/libyuv/include -I /root/firefox-clang/gfx/skia -I /root/firefox-clang/gfx/skia/skia -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/cloudproviders -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gtk-3.0/unix-print -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-shorten-64-to-32 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-27-100320-3286336-1 -x c++ /root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp

/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp

1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7#include "DrawTargetWebglInternal.h"
8#include "SourceSurfaceWebgl.h"
9
10#include "mozilla/ClearOnShutdown.h"
11#include "mozilla/StaticPrefs_gfx.h"
12#include "mozilla/gfx/AAStroke.h"
13#include "mozilla/gfx/Blur.h"
14#include "mozilla/gfx/DrawTargetSkia.h"
15#include "mozilla/gfx/gfxVars.h"
16#include "mozilla/gfx/Helpers.h"
17#include "mozilla/gfx/HelpersSkia.h"
18#include "mozilla/gfx/Logging.h"
19#include "mozilla/gfx/PathHelpers.h"
20#include "mozilla/gfx/PathSkia.h"
21#include "mozilla/gfx/Swizzle.h"
22#include "mozilla/layers/ImageDataSerializer.h"
23#include "mozilla/layers/RemoteTextureMap.h"
24#include "mozilla/widget/ScreenManager.h"
25#include "skia/include/core/SkPixmap.h"
26#include "nsContentUtils.h"
27#include "nsIMemoryReporter.h"
28
29#include "GLContext.h"
30#include "WebGLContext.h"
31#include "WebGLChild.h"
32#include "WebGLBuffer.h"
33#include "WebGLFramebuffer.h"
34#include "WebGLProgram.h"
35#include "WebGLShader.h"
36#include "WebGLTexture.h"
37#include "WebGLVertexArray.h"
38
39#include "gfxPlatform.h"
40
41#ifdef XP_MACOSX
42# include "mozilla/gfx/ScaledFontMac.h"
43#endif
44
45namespace mozilla::gfx {
46
47static Atomic<size_t> gReportedTextureMemory;
48static Atomic<size_t> gReportedHeapData;
49static Atomic<size_t> gReportedContextCount;
50static Atomic<size_t> gReportedTargetCount;
51
52class AcceleratedCanvas2DMemoryReporter final : public nsIMemoryReporter {
53 ~AcceleratedCanvas2DMemoryReporter() = default;
54
55 public:
56 NS_DECL_THREADSAFE_ISUPPORTSpublic: virtual nsresult QueryInterface(const nsIID& aIID
, void** aInstancePtr) override; virtual MozExternalRefCountType
AddRef(void) override; virtual MozExternalRefCountType Release
(void) override; using HasThreadSafeRefCnt = std::true_type; protected
: ::mozilla::ThreadSafeAutoRefCnt mRefCnt; nsAutoOwningThread
_mOwningThread; public:
57
58 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)static size_t MallocSizeOfOnAlloc(const void* aPtr) { mozilla
::dmd::ReportOnAlloc(aPtr); return moz_malloc_size_of(aPtr); }
59 MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)static size_t MallocSizeOfOnFree(const void* aPtr) { return moz_malloc_size_of
(aPtr); }
60
61 NS_IMETHODvirtual nsresult CollectReports(nsIHandleReportCallback* aHandleReport,
62 nsISupports* aData, bool aAnonymize) override {
63 MOZ_COLLECT_REPORT("ac2d-texture-memory", KIND_OTHER, UNITS_BYTES,(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-texture-memory"
), KIND_OTHER, UNITS_BYTES, gReportedTextureMemory, nsLiteralCString
("GPU memory used by Accelerated Canvas2D textures."), aData)
64 gReportedTextureMemory,(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-texture-memory"
), KIND_OTHER, UNITS_BYTES, gReportedTextureMemory, nsLiteralCString
("GPU memory used by Accelerated Canvas2D textures."), aData)
65 "GPU memory used by Accelerated Canvas2D textures.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-texture-memory"
), KIND_OTHER, UNITS_BYTES, gReportedTextureMemory, nsLiteralCString
("GPU memory used by Accelerated Canvas2D textures."), aData)
;
66 MOZ_COLLECT_REPORT("explicit/ac2d/heap-resources", KIND_HEAP, UNITS_BYTES,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/ac2d/heap-resources"
), KIND_HEAP, UNITS_BYTES, gReportedHeapData, nsLiteralCString
("Heap overhead for Accelerated Canvas2D resources."), aData)
67 gReportedHeapData,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/ac2d/heap-resources"
), KIND_HEAP, UNITS_BYTES, gReportedHeapData, nsLiteralCString
("Heap overhead for Accelerated Canvas2D resources."), aData)
68 "Heap overhead for Accelerated Canvas2D resources.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/ac2d/heap-resources"
), KIND_HEAP, UNITS_BYTES, gReportedHeapData, nsLiteralCString
("Heap overhead for Accelerated Canvas2D resources."), aData)
;
69 MOZ_COLLECT_REPORT("ac2d-context-count", KIND_OTHER, UNITS_COUNT,(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-context-count"
), KIND_OTHER, UNITS_COUNT, gReportedContextCount, nsLiteralCString
("Number of Accelerated Canvas2D contexts."), aData)
70 gReportedContextCount,(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-context-count"
), KIND_OTHER, UNITS_COUNT, gReportedContextCount, nsLiteralCString
("Number of Accelerated Canvas2D contexts."), aData)
71 "Number of Accelerated Canvas2D contexts.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-context-count"
), KIND_OTHER, UNITS_COUNT, gReportedContextCount, nsLiteralCString
("Number of Accelerated Canvas2D contexts."), aData)
;
72 MOZ_COLLECT_REPORT("ac2d-target-count", KIND_OTHER, UNITS_COUNT,(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-target-count"
), KIND_OTHER, UNITS_COUNT, gReportedTargetCount, nsLiteralCString
("Number of Accelerated Canvas2D targets."), aData)
73 gReportedTargetCount,(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-target-count"
), KIND_OTHER, UNITS_COUNT, gReportedTargetCount, nsLiteralCString
("Number of Accelerated Canvas2D targets."), aData)
74 "Number of Accelerated Canvas2D targets.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("ac2d-target-count"
), KIND_OTHER, UNITS_COUNT, gReportedTargetCount, nsLiteralCString
("Number of Accelerated Canvas2D targets."), aData)
;
75 return NS_OK;
76 }
77
78 static void Register() {
79 static bool registered = false;
80 if (!registered) {
81 registered = true;
82 RegisterStrongMemoryReporter(new AcceleratedCanvas2DMemoryReporter);
83 }
84 }
85};
86
87NS_IMPL_ISUPPORTS(AcceleratedCanvas2DMemoryReporter, nsIMemoryReporter)MozExternalRefCountType AcceleratedCanvas2DMemoryReporter::AddRef
(void) { static_assert(!std::is_destructible_v<AcceleratedCanvas2DMemoryReporter
>, "Reference-counted class " "AcceleratedCanvas2DMemoryReporter"
" should not have a public destructor. " "Make this class's destructor non-public"
); do { static_assert( mozilla::detail::AssertionConditionType
<decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0"
" (" "illegal refcnt" ")", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 87); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
") (" "illegal refcnt" ")"); do { MOZ_CrashSequence(__null, 87
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); do { static_assert( mozilla::detail::AssertionConditionType
<decltype("AcceleratedCanvas2DMemoryReporter" != nullptr)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!("AcceleratedCanvas2DMemoryReporter" != nullptr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("\"AcceleratedCanvas2DMemoryReporter\" != nullptr"
" (" "Must specify a name" ")", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 87); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"AcceleratedCanvas2DMemoryReporter\" != nullptr"
") (" "Must specify a name" ")"); do { MOZ_CrashSequence(__null
, 87); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false); if (!mRefCnt.isThreadSafe) _mOwningThread.AssertOwnership
("AcceleratedCanvas2DMemoryReporter" " not thread-safe"); nsrefcnt
count = ++mRefCnt; NS_LogAddRef((this), (count), ("AcceleratedCanvas2DMemoryReporter"
), (uint32_t)(sizeof(*this))); return count; } MozExternalRefCountType
AcceleratedCanvas2DMemoryReporter::Release(void) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(int32_t
(mRefCnt) > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0"
" (" "dup release" ")", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 87); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
") (" "dup release" ")"); do { MOZ_CrashSequence(__null, 87)
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); do { static_assert( mozilla::detail::AssertionConditionType
<decltype("AcceleratedCanvas2DMemoryReporter" != nullptr)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!("AcceleratedCanvas2DMemoryReporter" != nullptr))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("\"AcceleratedCanvas2DMemoryReporter\" != nullptr"
" (" "Must specify a name" ")", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 87); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"AcceleratedCanvas2DMemoryReporter\" != nullptr"
") (" "Must specify a name" ")"); do { MOZ_CrashSequence(__null
, 87); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false); if (!mRefCnt.isThreadSafe) _mOwningThread.AssertOwnership
("AcceleratedCanvas2DMemoryReporter" " not thread-safe"); const
char* const nametmp = "AcceleratedCanvas2DMemoryReporter"; nsrefcnt
count = --mRefCnt; NS_LogRelease((this), (count), (nametmp))
; if (count == 0) { mRefCnt = 1; delete (this); return 0; } return
count; } nsresult AcceleratedCanvas2DMemoryReporter::QueryInterface
(const nsIID& aIID, void** aInstancePtr) { do { if (!(aInstancePtr
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!"
, "aInstancePtr", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 87); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE
; static_assert(1 > 0, "Need more arguments to NS_INTERFACE_TABLE"
); static const QITableEntry table[] = { {&mozilla::detail
::kImplementedIID<AcceleratedCanvas2DMemoryReporter, nsIMemoryReporter
>, int32_t( reinterpret_cast<char*>(static_cast<nsIMemoryReporter
*>((AcceleratedCanvas2DMemoryReporter*)0x1000)) - reinterpret_cast
<char*>((AcceleratedCanvas2DMemoryReporter*)0x1000))}, {
&mozilla::detail::kImplementedIID<AcceleratedCanvas2DMemoryReporter
, nsISupports>, int32_t(reinterpret_cast<char*>(static_cast
<nsISupports*>( static_cast<nsIMemoryReporter*>((
AcceleratedCanvas2DMemoryReporter*)0x1000))) - reinterpret_cast
<char*>((AcceleratedCanvas2DMemoryReporter*)0x1000))}, {
nullptr, 0 } } ; static_assert(std::size(table) > 1, "need at least 1 interface"
); rv = NS_TableDrivenQI(static_cast<void*>(this), aIID
, aInstancePtr, table); return rv; }
88
89BackingTexture::BackingTexture(const IntSize& aSize, SurfaceFormat aFormat,
90 const RefPtr<WebGLTexture>& aTexture)
91 : mSize(aSize), mFormat(aFormat), mTexture(aTexture) {}
92
93#ifdef XP_WIN
94// Work around buggy ANGLE/D3D drivers that may copy blocks of pixels outside
95// the row length. Extra space is reserved at the end of each row up to stride
96// alignment. This does not affect standalone textures.
97static const Etagere::AllocatorOptions kR8AllocatorOptions = {16, 1, 1, 0};
98#endif
99
100SharedTexture::SharedTexture(const IntSize& aSize, SurfaceFormat aFormat,
101 const RefPtr<WebGLTexture>& aTexture)
102 : BackingTexture(aSize, aFormat, aTexture),
103 mAtlasAllocator(
104#ifdef XP_WIN
105 aFormat == SurfaceFormat::A8
106 ? Etagere::etagere_atlas_allocator_with_options(
107 aSize.width, aSize.height, &kR8AllocatorOptions)
108 :
109#endif
110 Etagere::etagere_atlas_allocator_new(aSize.width, aSize.height)) {
111}
112
113SharedTexture::~SharedTexture() {
114 if (mAtlasAllocator) {
115 Etagere::etagere_atlas_allocator_delete(mAtlasAllocator);
116 mAtlasAllocator = nullptr;
117 }
118}
119
120SharedTextureHandle::SharedTextureHandle(Etagere::AllocationId aId,
121 const IntRect& aBounds,
122 SharedTexture* aTexture)
123 : mAllocationId(aId), mBounds(aBounds), mTexture(aTexture) {}
124
125already_AddRefed<SharedTextureHandle> SharedTexture::Allocate(
126 const IntSize& aSize) {
127 Etagere::Allocation alloc = {{0, 0, 0, 0}, Etagere::INVALID_ALLOCATION_ID};
128 if (!mAtlasAllocator ||
129 !Etagere::etagere_atlas_allocator_allocate(mAtlasAllocator, aSize.width,
130 aSize.height, &alloc) ||
131 alloc.id == Etagere::INVALID_ALLOCATION_ID) {
132 return nullptr;
133 }
134 RefPtr<SharedTextureHandle> handle = new SharedTextureHandle(
135 alloc.id,
136 IntRect(IntPoint(alloc.rectangle.min_x, alloc.rectangle.min_y), aSize),
137 this);
138 return handle.forget();
139}
140
141bool SharedTexture::Free(SharedTextureHandle& aHandle) {
142 if (aHandle.mTexture != this) {
143 return false;
144 }
145 if (aHandle.mAllocationId != Etagere::INVALID_ALLOCATION_ID) {
146 if (mAtlasAllocator) {
147 Etagere::etagere_atlas_allocator_deallocate(mAtlasAllocator,
148 aHandle.mAllocationId);
149 }
150 aHandle.mAllocationId = Etagere::INVALID_ALLOCATION_ID;
151 }
152 return true;
153}
154
155StandaloneTexture::StandaloneTexture(const IntSize& aSize,
156 SurfaceFormat aFormat,
157 const RefPtr<WebGLTexture>& aTexture)
158 : BackingTexture(aSize, aFormat, aTexture) {}
159
160DrawTargetWebgl::DrawTargetWebgl() = default;
161
162inline void SharedContextWebgl::ClearLastTexture(bool aFullClear) {
163 mLastTexture = nullptr;
164 if (aFullClear) {
165 mLastClipMask = nullptr;
166 }
167}
168
169// Attempts to clear the snapshot state. If the snapshot is only referenced by
170// this target, then it should simply be destroyed. If it is a WebGL surface in
171// use by something else, then special cleanup such as reusing the texture or
172// copy-on-write may be possible.
173void DrawTargetWebgl::ClearSnapshot(bool aCopyOnWrite, bool aNeedHandle) {
174 if (!mSnapshot) {
175 return;
176 }
177 mSharedContext->ClearLastTexture();
178 RefPtr<SourceSurfaceWebgl> snapshot = mSnapshot.forget();
179 if (snapshot->hasOneRef()) {
180 return;
181 }
182 if (aCopyOnWrite) {
183 // WebGL snapshots must be notified that the framebuffer contents will be
184 // changing so that it can copy the data.
185 snapshot->DrawTargetWillChange(aNeedHandle);
186 } else {
187 // If not copying, then give the backing texture to the surface for reuse.
188 snapshot->GiveTexture(
189 mSharedContext->WrapSnapshot(GetSize(), GetFormat(), mTex.forget()));
190 }
191}
192
193DrawTargetWebgl::~DrawTargetWebgl() {
194 ClearSnapshot(false);
195 if (mSharedContext) {
196 // Force any Skia snapshots to copy the shmem before it deallocs.
197 if (mSkia) {
198 mSkia->DetachAllSnapshots();
199 }
200 mSharedContext->ClearLastTexture(true);
201 if (mClipMask) {
202 mSharedContext->RemoveUntrackedTextureMemory(mClipMask);
203 mClipMask = nullptr;
204 }
205 mFramebuffer = nullptr;
206 if (mTex) {
207 mSharedContext->RemoveUntrackedTextureMemory(mTex);
208 mTex = nullptr;
209 }
210 mSharedContext->mDrawTargetCount--;
211 gReportedTargetCount--;
212 }
213}
214
215SharedContextWebgl::SharedContextWebgl() = default;
216
217SharedContextWebgl::~SharedContextWebgl() {
218 // Detect context loss before deletion.
219 if (mWebgl) {
220 ExitTlsScope();
221 mWebgl->ActiveTexture(0);
222 gReportedContextCount--;
223 }
224 if (mWGRPathBuilder) {
225 WGR::wgr_builder_release(mWGRPathBuilder);
226 mWGRPathBuilder = nullptr;
227 }
228 if (mWGROutputBuffer) {
229 RemoveHeapData(mWGROutputBuffer.get());
230 mWGROutputBuffer = nullptr;
231 }
232 if (mPathVertexBuffer) {
233 RemoveUntrackedTextureMemory(mPathVertexBuffer);
234 mPathVertexBuffer = nullptr;
235 }
236 ClearZeroBuffer();
237 ClearAllTextures();
238 UnlinkSurfaceTextures();
239 UnlinkGlyphCaches();
240}
241
242gl::GLContext* SharedContextWebgl::GetGLContext() {
243 return mWebgl ? mWebgl->GL() : nullptr;
244}
245
246void SharedContextWebgl::EnterTlsScope() {
247 if (mTlsScope.isSome()) {
248 return;
249 }
250 if (gl::GLContext* gl = GetGLContext()) {
251 mTlsScope = Some(gl->mUseTLSIsCurrent);
252 gl::GLContext::InvalidateCurrentContext();
253 gl->mUseTLSIsCurrent = true;
254 }
255}
256
257void SharedContextWebgl::ExitTlsScope() {
258 if (mTlsScope.isNothing()) {
259 return;
260 }
261 if (gl::GLContext* gl = GetGLContext()) {
262 gl->mUseTLSIsCurrent = mTlsScope.value();
263 }
264 mTlsScope = Nothing();
265}
266
267// Remove any SourceSurface user data associated with this TextureHandle.
268inline void SharedContextWebgl::UnlinkSurfaceTexture(
269 const RefPtr<TextureHandle>& aHandle) {
270 if (RefPtr<SourceSurface> surface = aHandle->GetSurface()) {
271 // Ensure any WebGL snapshot textures get unlinked.
272 if (surface->GetType() == SurfaceType::WEBGL) {
273 static_cast<SourceSurfaceWebgl*>(surface.get())->OnUnlinkTexture(this);
274 }
275 surface->RemoveUserData(aHandle->IsShadow() ? &mShadowTextureKey
276 : &mTextureHandleKey);
277 }
278}
279
280// Unlinks TextureHandles from any SourceSurface user data.
281void SharedContextWebgl::UnlinkSurfaceTextures() {
282 for (RefPtr<TextureHandle> handle = mTextureHandles.getFirst(); handle;
283 handle = handle->getNext()) {
284 UnlinkSurfaceTexture(handle);
285 }
286}
287
288// Unlinks GlyphCaches from any ScaledFont user data.
289void SharedContextWebgl::UnlinkGlyphCaches() {
290 GlyphCache* cache = mGlyphCaches.getFirst();
291 while (cache) {
292 ScaledFont* font = cache->GetFont();
293 // Access the next cache before removing the user data, as it might destroy
294 // the cache.
295 cache = cache->getNext();
296 font->RemoveUserData(&mGlyphCacheKey);
297 }
298}
299
300void SharedContextWebgl::OnMemoryPressure() { mShouldClearCaches = true; }
301
302void SharedContextWebgl::ClearCaches() {
303 OnMemoryPressure();
304 ClearCachesIfNecessary();
305}
306
307// Clear out the entire list of texture handles from any source.
308void SharedContextWebgl::ClearAllTextures() {
309 while (!mTextureHandles.isEmpty()) {
310 PruneTextureHandle(mTextureHandles.popLast());
311 --mNumTextureHandles;
312 }
313}
314
315static inline size_t TextureMemoryUsage(WebGLTexture* aTexture) {
316 return aTexture->MemoryUsage();
317}
318
319static inline size_t TextureMemoryUsage(WebGLBuffer* aBuffer) {
320 return aBuffer->ByteLength();
321}
322
323inline void SharedContextWebgl::AddHeapData(const void* aBuf) {
324 if (aBuf) {
325 gReportedHeapData +=
326 AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnAlloc(aBuf);
327 }
328}
329
330inline void SharedContextWebgl::RemoveHeapData(const void* aBuf) {
331 if (aBuf) {
332 gReportedHeapData -=
333 AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnFree(aBuf);
334 }
335}
336
337inline void SharedContextWebgl::AddUntrackedTextureMemory(size_t aBytes) {
338 gReportedTextureMemory += aBytes;
339}
340
341inline void SharedContextWebgl::RemoveUntrackedTextureMemory(size_t aBytes) {
342 gReportedTextureMemory -= aBytes;
343}
344
345template <typename T>
346inline void SharedContextWebgl::AddUntrackedTextureMemory(
347 const RefPtr<T>& aObject, size_t aBytes) {
348 size_t usedBytes = aBytes > 0 ? aBytes : TextureMemoryUsage(aObject);
349 AddUntrackedTextureMemory(usedBytes);
350 gReportedHeapData += aObject->SizeOfIncludingThis(
351 AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnAlloc);
352}
353
354template <typename T>
355inline void SharedContextWebgl::RemoveUntrackedTextureMemory(
356 const RefPtr<T>& aObject, size_t aBytes) {
357 size_t usedBytes = aBytes > 0 ? aBytes : TextureMemoryUsage(aObject);
358 RemoveUntrackedTextureMemory(usedBytes);
359 gReportedHeapData -= aObject->SizeOfIncludingThis(
360 AcceleratedCanvas2DMemoryReporter::MallocSizeOfOnFree);
361}
362
363inline void SharedContextWebgl::AddTextureMemory(BackingTexture* aTexture) {
364 size_t usedBytes = aTexture->UsedBytes();
365 mTotalTextureMemory += usedBytes;
366 AddUntrackedTextureMemory(aTexture->GetWebGLTexture(), usedBytes);
367}
368
369inline void SharedContextWebgl::RemoveTextureMemory(BackingTexture* aTexture) {
370 size_t usedBytes = aTexture->UsedBytes();
371 mTotalTextureMemory -= usedBytes;
372 RemoveUntrackedTextureMemory(aTexture->GetWebGLTexture(), usedBytes);
373}
374
375// Scan through the shared texture pages looking for any that are empty and
376// delete them.
377void SharedContextWebgl::ClearEmptyTextureMemory() {
378 for (auto pos = mSharedTextures.begin(); pos != mSharedTextures.end();) {
379 if (!(*pos)->HasAllocatedHandles()) {
380 RefPtr<SharedTexture> shared = *pos;
381 mEmptyTextureMemory -= shared->UsedBytes();
382 RemoveTextureMemory(shared);
383 pos = mSharedTextures.erase(pos);
384 } else {
385 ++pos;
386 }
387 }
388}
389
390void SharedContextWebgl::ClearZeroBuffer() {
391 if (mZeroBuffer) {
392 RemoveUntrackedTextureMemory(mZeroBuffer);
393 mZeroBuffer = nullptr;
394 }
395}
396
397// If there is a request to clear out the caches because of memory pressure,
398// then first clear out all the texture handles in the texture cache. If there
399// are still empty texture pages being kept around, then clear those too.
400void SharedContextWebgl::ClearCachesIfNecessary() {
401 if (!mShouldClearCaches.exchange(false)) {
402 return;
403 }
404 ClearZeroBuffer();
405 ClearAllTextures();
406 if (mEmptyTextureMemory) {
407 ClearEmptyTextureMemory();
408 }
409 ClearLastTexture();
410}
411
412// Try to initialize a new WebGL context. Verifies that the requested size does
413// not exceed the available texture limits and that shader creation succeeded.
414bool DrawTargetWebgl::Init(const IntSize& size, const SurfaceFormat format,
415 const RefPtr<SharedContextWebgl>& aSharedContext) {
416 MOZ_ASSERT(format == SurfaceFormat::B8G8R8A8 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat
::B8G8R8X8)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(format == SurfaceFormat::B8G8R8A8 ||
format == SurfaceFormat::B8G8R8X8))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::B8G8R8X8"
, "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 417);
AnnotateMozCrashReason("MOZ_ASSERT" "(" "format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::B8G8R8X8"
")"); do { MOZ_CrashSequence(__null, 417); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
417 format == SurfaceFormat::B8G8R8X8)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat
::B8G8R8X8)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(format == SurfaceFormat::B8G8R8A8 ||
format == SurfaceFormat::B8G8R8X8))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::B8G8R8X8"
, "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 417);
AnnotateMozCrashReason("MOZ_ASSERT" "(" "format == SurfaceFormat::B8G8R8A8 || format == SurfaceFormat::B8G8R8X8"
")"); do { MOZ_CrashSequence(__null, 417); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
418
419 mSize = size;
420 mFormat = format;
421
422 if (!aSharedContext || aSharedContext->IsContextLost() ||
423 aSharedContext->mDrawTargetCount >=
424 StaticPrefs::gfx_canvas_accelerated_max_draw_target_count()) {
425 return false;
426 }
427 mSharedContext = aSharedContext;
428 mSharedContext->mDrawTargetCount++;
429 gReportedTargetCount++;
430
431 if (size_t(std::max(size.width, size.height)) >
432 mSharedContext->mMaxTextureSize) {
433 return false;
434 }
435
436 if (!CreateFramebuffer()) {
437 return false;
438 }
439
440 size_t byteSize = layers::ImageDataSerializer::ComputeRGBBufferSize(
441 mSize, SurfaceFormat::B8G8R8A8);
442 if (byteSize == 0) {
443 return false;
444 }
445
446 size_t shmemSize = mozilla::ipc::shared_memory::PageAlignedSize(byteSize);
447 if (NS_WARN_IF(shmemSize > UINT32_MAX)NS_warn_if_impl(shmemSize > (4294967295U), "shmemSize > UINT32_MAX"
, "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 447)
) {
448 MOZ_ASSERT_UNREACHABLE("Buffer too big?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Buffer too big?" ")", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Buffer too big?" ")"); do { MOZ_CrashSequence
(__null, 448); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
449 return false;
450 }
451
452 auto handle = mozilla::ipc::shared_memory::Create(shmemSize);
453 if (NS_WARN_IF(!handle)NS_warn_if_impl(!handle, "!handle", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 453)
) {
454 return false;
455 }
456 auto mapping = handle.Map();
457 if (NS_WARN_IF(!mapping)NS_warn_if_impl(!mapping, "!mapping", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 457)
) {
458 return false;
459 }
460
461 mShmemHandle = std::move(handle).ToReadOnly();
462 mShmem = std::move(mapping);
463
464 mSkia = new DrawTargetSkia;
465 auto stride = layers::ImageDataSerializer::ComputeRGBStride(
466 SurfaceFormat::B8G8R8A8, size.width);
467 if (!mSkia->Init(mShmem.DataAs<uint8_t>(), size, stride,
468 SurfaceFormat::B8G8R8A8, true)) {
469 return false;
470 }
471
472 // Allocate an unclipped copy of the DT pointing to its data.
473 uint8_t* dtData = nullptr;
474 IntSize dtSize;
475 int32_t dtStride = 0;
476 SurfaceFormat dtFormat = SurfaceFormat::UNKNOWN;
477 if (!mSkia->LockBits(&dtData, &dtSize, &dtStride, &dtFormat)) {
478 return false;
479 }
480 mSkiaNoClip = new DrawTargetSkia;
481 if (!mSkiaNoClip->Init(dtData, dtSize, dtStride, dtFormat, true)) {
482 mSkia->ReleaseBits(dtData);
483 return false;
484 }
485 mSkia->ReleaseBits(dtData);
486
487 SetPermitSubpixelAA(IsOpaque(format));
488 return true;
489}
490
491// If a non-recoverable error occurred that would stop the canvas from initing.
492static Atomic<bool> sContextInitError(false);
493
494already_AddRefed<SharedContextWebgl> SharedContextWebgl::Create() {
495 // If context initialization would fail, don't even try to create a context.
496 if (sContextInitError) {
497 return nullptr;
498 }
499 RefPtr<SharedContextWebgl> sharedContext = new SharedContextWebgl;
500 if (!sharedContext->Initialize()) {
501 return nullptr;
502 }
503 return sharedContext.forget();
504}
505
506bool SharedContextWebgl::Initialize() {
507 AcceleratedCanvas2DMemoryReporter::Register();
508
509 WebGLContextOptions options = {};
510 options.alpha = true;
511 options.depth = false;
512 options.stencil = false;
513 options.antialias = false;
514 options.preserveDrawingBuffer = true;
515 options.failIfMajorPerformanceCaveat = false;
516
517 const bool resistFingerprinting = nsContentUtils::ShouldResistFingerprinting(
518 "Fallback", RFPTarget::WebGLRenderCapability);
519 const auto initDesc = webgl::InitContextDesc{
520 .isWebgl2 = true,
521 .resistFingerprinting = resistFingerprinting,
522 .principalKey = 0,
523 .size = {1, 1},
524 .options = options,
525 };
526
527 webgl::InitContextResult initResult;
528 mWebgl = WebGLContext::Create(nullptr, initDesc, &initResult);
529 if (!mWebgl) {
530 // There was a non-recoverable error when trying to create a host context.
531 sContextInitError = true;
532 mWebgl = nullptr;
533 return false;
534 }
535 if (mWebgl->IsContextLost()) {
536 mWebgl = nullptr;
537 return false;
538 }
539
540 mMaxTextureSize = initResult.limits.maxTex2dSize;
541
542 if (kIsMacOS) {
543 mRasterizationTruncates = initResult.vendor == gl::GLVendor::ATI;
544 }
545
546 CachePrefs();
547
548 if (!CreateShaders()) {
549 // There was a non-recoverable error when trying to init shaders.
550 sContextInitError = true;
551 mWebgl = nullptr;
552 return false;
553 }
554
555 mWGRPathBuilder = WGR::wgr_new_builder();
556
557 gReportedContextCount++;
558
559 return true;
560}
561
562inline void SharedContextWebgl::BlendFunc(GLenum aSrcFactor,
563 GLenum aDstFactor) {
564 mWebgl->BlendFuncSeparate({}, aSrcFactor, aDstFactor, aSrcFactor, aDstFactor);
565}
566
567void SharedContextWebgl::SetBlendState(CompositionOp aOp,
568 const Maybe<DeviceColor>& aColor) {
569 if (aOp == mLastCompositionOp && mLastBlendColor == aColor) {
570 return;
571 }
572 mLastCompositionOp = aOp;
573 mLastBlendColor = aColor;
574 // AA is not supported for all composition ops, so switching blend modes may
575 // cause a toggle in AA state. Certain ops such as OP_SOURCE require output
576 // alpha that is blended separately from AA coverage. This would require two
577 // stage blending which can incur a substantial performance penalty, so to
578 // work around this currently we just disable AA for those ops.
579
580 // Map the composition op to a WebGL blend mode, if possible.
581 bool enabled = true;
582 switch (aOp) {
583 case CompositionOp::OP_OVER:
584 if (aColor) {
585 // If a color is supplied, then we blend subpixel text.
586 mWebgl->BlendColor(aColor->b, aColor->g, aColor->r, 1.0f);
587 BlendFunc(LOCAL_GL_CONSTANT_COLOR0x8001, LOCAL_GL_ONE_MINUS_SRC_COLOR0x0301);
588 } else {
589 BlendFunc(LOCAL_GL_ONE1, LOCAL_GL_ONE_MINUS_SRC_ALPHA0x0303);
590 }
591 break;
592 case CompositionOp::OP_ADD:
593 BlendFunc(LOCAL_GL_ONE1, LOCAL_GL_ONE1);
594 break;
595 case CompositionOp::OP_ATOP:
596 BlendFunc(LOCAL_GL_DST_ALPHA0x0304, LOCAL_GL_ONE_MINUS_SRC_ALPHA0x0303);
597 break;
598 case CompositionOp::OP_SOURCE:
599 if (aColor) {
600 // If a color is supplied, then we assume there is clipping or AA. This
601 // requires that we still use an over blend func with the clip/AA alpha,
602 // while filling the interior with the unaltered color. Normally this
603 // would require dual source blending, but we can emulate it with only
604 // a blend color.
605 mWebgl->BlendColor(aColor->b, aColor->g, aColor->r, aColor->a);
606 BlendFunc(LOCAL_GL_CONSTANT_COLOR0x8001, LOCAL_GL_ONE_MINUS_SRC_COLOR0x0301);
607 } else {
608 enabled = false;
609 }
610 break;
611 case CompositionOp::OP_CLEAR:
612 // Assume the source is an alpha mask for clearing. Be careful to blend in
613 // the correct alpha if the target is opaque.
614 mWebgl->BlendFuncSeparate(
615 {}, LOCAL_GL_ZERO0, LOCAL_GL_ONE_MINUS_SRC_ALPHA0x0303,
616 IsOpaque(mCurrentTarget->GetFormat()) ? LOCAL_GL_ONE1 : LOCAL_GL_ZERO0,
617 LOCAL_GL_ONE_MINUS_SRC_ALPHA0x0303);
618 break;
619 default:
620 enabled = false;
621 break;
622 }
623
624 mWebgl->SetEnabled(LOCAL_GL_BLEND0x0BE2, {}, enabled);
625}
626
627// Ensure the WebGL framebuffer is set to the current target.
628bool SharedContextWebgl::SetTarget(DrawTargetWebgl* aDT) {
629 if (!mWebgl || mWebgl->IsContextLost()) {
630 return false;
631 }
632 if (aDT != mCurrentTarget) {
633 mCurrentTarget = aDT;
634 if (aDT) {
635 mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, aDT->mFramebuffer);
636 mViewportSize = aDT->GetSize();
637 mWebgl->Viewport(0, 0, mViewportSize.width, mViewportSize.height);
638 }
639 }
640 return true;
641}
642
643// Replace the current clip rect with a new potentially-AA'd clip rect.
644void SharedContextWebgl::SetClipRect(const Rect& aClipRect) {
645 // Only invalidate the clip rect if it actually changes.
646 if (!mClipAARect.IsEqualEdges(aClipRect)) {
647 mClipAARect = aClipRect;
648 // Store the integer-aligned bounds.
649 mClipRect = RoundedOut(aClipRect);
650 }
651}
652
653bool SharedContextWebgl::SetClipMask(const RefPtr<WebGLTexture>& aTex) {
654 if (mLastClipMask != aTex) {
655 if (!mWebgl) {
656 return false;
657 }
658 mWebgl->ActiveTexture(1);
659 mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, aTex);
660 mWebgl->ActiveTexture(0);
661 mLastClipMask = aTex;
662 }
663 return true;
664}
665
666bool SharedContextWebgl::SetNoClipMask() {
667 if (mNoClipMask) {
668 return SetClipMask(mNoClipMask);
669 }
670 if (!mWebgl) {
671 return false;
672 }
673 mNoClipMask = mWebgl->CreateTexture();
674 if (!mNoClipMask) {
675 return false;
676 }
677 mWebgl->ActiveTexture(1);
678 mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, mNoClipMask);
679 static const auto solidMask =
680 std::array<const uint8_t, 4>{0xFF, 0xFF, 0xFF, 0xFF};
681 mWebgl->TexImage(0, LOCAL_GL_RGBA80x8058, {0, 0, 0},
682 {LOCAL_GL_RGBA0x1908, LOCAL_GL_UNSIGNED_BYTE0x1401},
683 {LOCAL_GL_TEXTURE_2D0x0DE1,
684 {1, 1, 1},
685 gfxAlphaType::NonPremult,
686 Some(Span{solidMask})});
687 InitTexParameters(mNoClipMask, false);
688 mWebgl->ActiveTexture(0);
689 mLastClipMask = mNoClipMask;
690 return true;
691}
692
693inline bool DrawTargetWebgl::ClipStack::operator==(
694 const DrawTargetWebgl::ClipStack& aOther) const {
695 // Verify the transform and bounds match.
696 if (!mTransform.FuzzyEquals(aOther.mTransform) ||
697 !mRect.IsEqualInterior(aOther.mRect)) {
698 return false;
699 }
700 // Verify the paths match.
701 if (!mPath) {
702 return !aOther.mPath;
703 }
704 if (!aOther.mPath ||
705 mPath->GetBackendType() != aOther.mPath->GetBackendType()) {
706 return false;
707 }
708 if (mPath->GetBackendType() != BackendType::SKIA) {
709 return mPath == aOther.mPath;
710 }
711 return static_cast<const PathSkia*>(mPath.get())->GetPath() ==
712 static_cast<const PathSkia*>(aOther.mPath.get())->GetPath();
713}
714
715// If the clip region can't be approximated by a simple clip rect, then we need
716// to generate a clip mask that can represent the clip region per-pixel. We
717// render to the Skia target temporarily, transparent outside the clip region,
718// opaque inside, and upload this to a texture that can be used by the shaders.
719bool DrawTargetWebgl::GenerateComplexClipMask() {
720 if (!mClipChanged || (mClipMask && mCachedClipStack == mClipStack)) {
721 mClipChanged = false;
722 // If the clip mask was already generated, use the cached mask and bounds.
723 mSharedContext->SetClipMask(mClipMask);
724 mSharedContext->SetClipRect(mClipBounds);
725 return true;
726 }
727 if (!mWebglValid) {
728 // If the Skia target is currently being used, then we can't render the mask
729 // in it.
730 return false;
731 }
732 RefPtr<WebGLContext> webgl = mSharedContext->mWebgl;
733 if (!webgl) {
734 return false;
735 }
736 bool init = false;
737 if (!mClipMask) {
738 mClipMask = webgl->CreateTexture();
739 if (!mClipMask) {
740 return false;
741 }
742 init = true;
743 }
744 // Try to get the bounds of the clip to limit the size of the mask.
745 if (Maybe<IntRect> clip = mSkia->GetDeviceClipRect(true)) {
746 mClipBounds = *clip;
747 } else {
748 // If we can't get bounds, then just use the entire viewport.
749 mClipBounds = GetRect();
750 }
751 mClipAARect = Rect(mClipBounds);
752 // If initializing the clip mask, then allocate the entire texture to ensure
753 // all pixels get filled with an empty mask regardless. Otherwise, restrict
754 // uploading to only the clip region.
755 RefPtr<DrawTargetSkia> dt = new DrawTargetSkia;
756 if (!dt->Init(mClipBounds.Size(), SurfaceFormat::A8)) {
757 if (init) {
758 // Ensure that the clip mask is reinitialized on the next attempt.
759 mClipMask = nullptr;
760 }
761 return false;
762 }
763 // Set the clip region and fill the entire inside of it
764 // with opaque white.
765 mCachedClipStack.clear();
766 for (auto& clipStack : mClipStack) {
767 // Record the current state of the clip stack for this mask.
768 mCachedClipStack.push_back(clipStack);
769 dt->SetTransform(
770 Matrix(clipStack.mTransform).PostTranslate(-mClipBounds.TopLeft()));
771 if (clipStack.mPath) {
772 dt->PushClip(clipStack.mPath);
773 } else {
774 dt->PushClipRect(clipStack.mRect);
775 }
776 }
777 dt->SetTransform(Matrix::Translation(-mClipBounds.TopLeft()));
778 dt->FillRect(Rect(mClipBounds), ColorPattern(DeviceColor(1, 1, 1, 1)));
779 // Bind the clip mask for uploading. This is done on texture unit 0 so that
780 // we can work around an Windows Intel driver bug. If done on texture unit 1,
781 // the driver doesn't notice that the texture contents was modified. Force a
782 // re-latch by binding the texture on texture unit 1 only after modification.
783 webgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, mClipMask);
784 if (init) {
785 mSharedContext->InitTexParameters(mClipMask, false);
786 }
787 RefPtr<DataSourceSurface> data;
788 if (RefPtr<SourceSurface> snapshot = dt->Snapshot()) {
789 data = snapshot->GetDataSurface();
790 }
791 // Finally, upload the texture data and initialize texture storage if
792 // necessary.
793 if (init && mClipBounds.Size() != mSize) {
794 mSharedContext->UploadSurface(nullptr, SurfaceFormat::A8, GetRect(),
795 IntPoint(), true, true);
796 init = false;
797 }
798 mSharedContext->UploadSurface(data, SurfaceFormat::A8,
799 IntRect(IntPoint(), mClipBounds.Size()),
800 mClipBounds.TopLeft(), init);
801 mSharedContext->ClearLastTexture();
802 // Bind the new clip mask to the clip sampler on texture unit 1.
803 mSharedContext->SetClipMask(mClipMask);
804 mSharedContext->SetClipRect(mClipBounds);
805 // We uploaded a surface, just as if we missed the texture cache, so account
806 // for that here.
807 if (init) {
808 mSharedContext->AddUntrackedTextureMemory(mClipMask);
809 }
810 mProfile.OnCacheMiss();
811 return !!data;
812}
813
814bool DrawTargetWebgl::SetSimpleClipRect() {
815 // Determine whether the clipping rectangle is simple enough to accelerate.
816 // Check if there is a device space clip rectangle available from the Skia
817 // target.
818 if (Maybe<IntRect> clip = mSkia->GetDeviceClipRect(false)) {
819 // If the clip is empty, leave the final integer clip rectangle empty to
820 // trivially discard the draw request.
821 // If the clip rect is larger than the viewport, just set it to the
822 // viewport.
823 if (!clip->IsEmpty() && clip->Contains(GetRect())) {
824 clip = Some(GetRect());
825 }
826 mSharedContext->SetClipRect(*clip);
827 mSharedContext->SetNoClipMask();
828 return true;
829 }
830
831 // There was no pixel-aligned clip rect available, so check the clip stack to
832 // see if there is an AA'd axis-aligned rectangle clip.
833 Rect rect(GetRect());
834 for (auto& clipStack : mClipStack) {
835 // If clip is a path or it has a non-axis-aligned transform, then it is
836 // complex.
837 if (clipStack.mPath ||
838 !clipStack.mTransform.PreservesAxisAlignedRectangles()) {
839 return false;
840 }
841 // Transform the rect and intersect it with the current clip.
842 rect =
843 clipStack.mTransform.TransformBounds(clipStack.mRect).Intersect(rect);
844 }
845 mSharedContext->SetClipRect(rect);
846 mSharedContext->SetNoClipMask();
847 return true;
848}
849
850// Installs the Skia clip rectangle, if applicable, onto the shared WebGL
851// context as well as sets the WebGL framebuffer to the current target.
852bool DrawTargetWebgl::PrepareContext(bool aClipped) {
853 if (!aClipped) {
854 // If no clipping requested, just set the clip rect to the viewport.
855 mSharedContext->SetClipRect(GetRect());
856 mSharedContext->SetNoClipMask();
857 // Ensure the clip gets reset if clipping is later requested for the target.
858 mRefreshClipState = true;
859 } else if (mRefreshClipState || !mSharedContext->IsCurrentTarget(this)) {
860 // Try to use a simple clip rect if possible. Otherwise, fall back to
861 // generating a clip mask texture that can represent complex clip regions.
862 if (!SetSimpleClipRect() && !GenerateComplexClipMask()) {
863 return false;
864 }
865 mClipChanged = false;
866 mRefreshClipState = false;
867 }
868 return mSharedContext->SetTarget(this);
869}
870
871bool SharedContextWebgl::IsContextLost() const {
872 return !mWebgl || mWebgl->IsContextLost();
873}
874
875// Signal to CanvasRenderingContext2D when the WebGL context is lost.
876bool DrawTargetWebgl::IsValid() const {
877 return mSharedContext && !mSharedContext->IsContextLost();
878}
879
880bool DrawTargetWebgl::CanCreate(const IntSize& aSize, SurfaceFormat aFormat) {
881 if (!gfxVars::UseAcceleratedCanvas2D()) {
882 return false;
883 }
884
885 if (!Factory::AllowedSurfaceSize(aSize)) {
886 return false;
887 }
888
889 // The interpretation of the min-size and max-size follows from the old
890 // SkiaGL prefs. First just ensure that the context is not unreasonably
891 // small.
892 static const int32_t kMinDimension = 16;
893 if (std::min(aSize.width, aSize.height) < kMinDimension) {
894 return false;
895 }
896
897 int32_t minSize = StaticPrefs::gfx_canvas_accelerated_min_size();
898 if (aSize.width * aSize.height < minSize * minSize) {
899 return false;
900 }
901
902 // Maximum pref allows 3 different options:
903 // 0 means unlimited size,
904 // > 0 means use value as an absolute threshold,
905 // < 0 means use the number of screen pixels as a threshold.
906 int32_t maxSize = StaticPrefs::gfx_canvas_accelerated_max_size();
907 if (maxSize > 0) {
908 if (std::max(aSize.width, aSize.height) > maxSize) {
909 return false;
910 }
911 } else if (maxSize < 0) {
912 // Default to historical mobile screen size of 980x480, like FishIEtank.
913 // In addition, allow acceleration up to this size even if the screen is
914 // smaller. A lot content expects this size to work well. See Bug 999841
915 static const int32_t kScreenPixels = 980 * 480;
916
917 if (RefPtr<widget::Screen> screen =
918 widget::ScreenManager::GetSingleton().GetPrimaryScreen()) {
919 LayoutDeviceIntSize screenSize = screen->GetRect().Size();
920 if (aSize.width * aSize.height >
921 std::max(screenSize.width * screenSize.height, kScreenPixels)) {
922 return false;
923 }
924 }
925 }
926
927 return true;
928}
929
930already_AddRefed<DrawTargetWebgl> DrawTargetWebgl::Create(
931 const IntSize& aSize, SurfaceFormat aFormat,
932 const RefPtr<SharedContextWebgl>& aSharedContext) {
933 // Validate the size and format.
934 if (!CanCreate(aSize, aFormat)) {
935 return nullptr;
936 }
937
938 RefPtr<DrawTargetWebgl> dt = new DrawTargetWebgl;
939 if (!dt->Init(aSize, aFormat, aSharedContext) || !dt->IsValid()) {
940 return nullptr;
941 }
942
943 return dt.forget();
944}
945
946void* DrawTargetWebgl::GetNativeSurface(NativeSurfaceType aType) {
947 switch (aType) {
948 case NativeSurfaceType::WEBGL_CONTEXT:
949 // If the context is lost, then don't attempt to access it.
950 if (mSharedContext->IsContextLost()) {
951 return nullptr;
952 }
953 if (!mWebglValid) {
954 FlushFromSkia();
955 }
956 return mSharedContext->mWebgl.get();
957 default:
958 return nullptr;
959 }
960}
961
962// Wrap a WebGL texture holding a snapshot with a texture handle. Note that
963// while the texture is still in use as the backing texture of a framebuffer,
964// it's texture memory is not currently tracked with other texture handles.
965// Once it is finally orphaned and used as a texture handle, it must be added
966// to the resource usage totals.
967already_AddRefed<TextureHandle> SharedContextWebgl::WrapSnapshot(
968 const IntSize& aSize, SurfaceFormat aFormat, RefPtr<WebGLTexture> aTex) {
969 // Ensure there is enough space for the texture.
970 size_t usedBytes = BackingTexture::UsedBytes(aFormat, aSize);
971 PruneTextureMemory(usedBytes, false);
972 // Allocate a handle for the texture
973 RefPtr<StandaloneTexture> handle =
974 new StandaloneTexture(aSize, aFormat, aTex.forget());
975 mStandaloneTextures.push_back(handle);
976 mTextureHandles.insertFront(handle);
977 AddTextureMemory(handle);
978 mUsedTextureMemory += usedBytes;
979 ++mNumTextureHandles;
980 return handle.forget();
981}
982
983void SharedContextWebgl::SetTexFilter(WebGLTexture* aTex, bool aFilter) {
984 mWebgl->TexParameter_base(
985 LOCAL_GL_TEXTURE_2D0x0DE1, LOCAL_GL_TEXTURE_MAG_FILTER0x2800,
986 FloatOrInt(aFilter ? LOCAL_GL_LINEAR0x2601 : LOCAL_GL_NEAREST0x2600));
987 mWebgl->TexParameter_base(
988 LOCAL_GL_TEXTURE_2D0x0DE1, LOCAL_GL_TEXTURE_MIN_FILTER0x2801,
989 FloatOrInt(aFilter ? LOCAL_GL_LINEAR0x2601 : LOCAL_GL_NEAREST0x2600));
990}
991
992void SharedContextWebgl::InitTexParameters(WebGLTexture* aTex, bool aFilter) {
993 mWebgl->TexParameter_base(LOCAL_GL_TEXTURE_2D0x0DE1, LOCAL_GL_TEXTURE_WRAP_S0x2802,
994 FloatOrInt(LOCAL_GL_REPEAT0x2901));
995 mWebgl->TexParameter_base(LOCAL_GL_TEXTURE_2D0x0DE1, LOCAL_GL_TEXTURE_WRAP_T0x2803,
996 FloatOrInt(LOCAL_GL_REPEAT0x2901));
997 SetTexFilter(aTex, aFilter);
998}
999
1000// Copy the contents of the WebGL framebuffer into a WebGL texture.
1001already_AddRefed<TextureHandle> SharedContextWebgl::CopySnapshot(
1002 const IntRect& aRect, TextureHandle* aHandle) {
1003 if (!mWebgl || mWebgl->IsContextLost()) {
1004 return nullptr;
1005 }
1006
1007 // If the target is going away, then we can just directly reuse the
1008 // framebuffer texture since it will never change.
1009 RefPtr<WebGLTexture> tex = mWebgl->CreateTexture();
1010 if (!tex) {
1011 return nullptr;
1012 }
1013
1014 // If copying from a non-DT source, we have to bind a scratch framebuffer for
1015 // reading.
1016 if (aHandle) {
1017 if (!mScratchFramebuffer) {
1018 mScratchFramebuffer = mWebgl->CreateFramebuffer();
1019 }
1020 mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, mScratchFramebuffer);
1021
1022 webgl::FbAttachInfo attachInfo;
1023 attachInfo.tex = aHandle->GetBackingTexture()->GetWebGLTexture();
1024 mWebgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER0x8D40, LOCAL_GL_COLOR_ATTACHMENT00x8CE0,
1025 LOCAL_GL_TEXTURE_2D0x0DE1, attachInfo);
1026 }
1027
1028 // Create a texture to hold the copy
1029 mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, tex);
1030 mWebgl->TexStorage(LOCAL_GL_TEXTURE_2D0x0DE1, 1, LOCAL_GL_RGBA80x8058,
1031 {uint32_t(aRect.width), uint32_t(aRect.height), 1});
1032 InitTexParameters(tex);
1033 // Copy the framebuffer into the texture
1034 mWebgl->CopyTexImage(LOCAL_GL_TEXTURE_2D0x0DE1, 0, 0, {0, 0, 0}, {aRect.x, aRect.y},
1035 {uint32_t(aRect.width), uint32_t(aRect.height)});
1036 ClearLastTexture();
1037
1038 SurfaceFormat format =
1039 aHandle ? aHandle->GetFormat() : mCurrentTarget->GetFormat();
1040 already_AddRefed<TextureHandle> result =
1041 WrapSnapshot(aRect.Size(), format, tex.forget());
1042
1043 // Restore the actual framebuffer after reading is done.
1044 if (aHandle && mCurrentTarget) {
1045 mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, mCurrentTarget->mFramebuffer);
1046 }
1047
1048 return result;
1049}
1050
1051inline DrawTargetWebgl::AutoRestoreContext::AutoRestoreContext(
1052 DrawTargetWebgl* aTarget)
1053 : mTarget(aTarget),
1054 mClipAARect(aTarget->mSharedContext->mClipAARect),
1055 mLastClipMask(aTarget->mSharedContext->mLastClipMask) {}
1056
1057inline DrawTargetWebgl::AutoRestoreContext::~AutoRestoreContext() {
1058 mTarget->mSharedContext->SetClipRect(mClipAARect);
1059 if (mLastClipMask) {
1060 mTarget->mSharedContext->SetClipMask(mLastClipMask);
1061 }
1062 mTarget->mRefreshClipState = true;
1063}
1064
1065// Utility method to install the target before copying a snapshot.
1066already_AddRefed<TextureHandle> DrawTargetWebgl::CopySnapshot(
1067 const IntRect& aRect) {
1068 AutoRestoreContext restore(this);
1069 if (!PrepareContext(false)) {
1070 return nullptr;
1071 }
1072 return mSharedContext->CopySnapshot(aRect);
1073}
1074
1075bool DrawTargetWebgl::HasDataSnapshot() const {
1076 return (mSkiaValid && !mSkiaLayer) || (mSnapshot && mSnapshot->HasReadData());
1077}
1078
1079bool DrawTargetWebgl::PrepareSkia() {
1080 if (!mSkiaValid) {
1081 ReadIntoSkia();
1082 } else if (mSkiaLayer) {
1083 FlattenSkia();
1084 }
1085 return mSkiaValid;
1086}
1087
1088bool DrawTargetWebgl::EnsureDataSnapshot() {
1089 return HasDataSnapshot() || PrepareSkia();
1090}
1091
1092void DrawTargetWebgl::PrepareShmem() { PrepareSkia(); }
1093
1094// Borrow a snapshot that may be used by another thread for composition. Only
1095// Skia snapshots are safe to pass around.
1096already_AddRefed<SourceSurface> DrawTargetWebgl::GetDataSnapshot() {
1097 PrepareSkia();
1098 return mSkia->Snapshot(mFormat);
1099}
1100
1101already_AddRefed<SourceSurface> DrawTargetWebgl::Snapshot() {
1102 // If already using the Skia fallback, then just snapshot that.
1103 if (mSkiaValid) {
1104 return GetDataSnapshot();
1105 }
1106
1107 // There's no valid Skia snapshot, so we need to get one from the WebGL
1108 // context.
1109 if (!mSnapshot) {
1110 // Create a copy-on-write reference to this target.
1111 mSnapshot = new SourceSurfaceWebgl(this);
1112 }
1113 return do_AddRef(mSnapshot);
1114}
1115
1116// If we need to provide a snapshot for another DrawTargetWebgl that shares the
1117// same WebGL context, then it is safe to directly return a snapshot. Otherwise,
1118// we may be exporting to another thread and require a data snapshot.
1119already_AddRefed<SourceSurface> DrawTargetWebgl::GetOptimizedSnapshot(
1120 DrawTarget* aTarget) {
1121 if (aTarget && aTarget->GetBackendType() == BackendType::WEBGL &&
1122 static_cast<DrawTargetWebgl*>(aTarget)->mSharedContext ==
1123 mSharedContext) {
1124 return Snapshot();
1125 }
1126 return GetDataSnapshot();
1127}
1128
1129// Read from the WebGL context into a buffer. This handles both swizzling BGRA
1130// to RGBA and flipping the image.
1131bool SharedContextWebgl::ReadInto(uint8_t* aDstData, int32_t aDstStride,
1132 SurfaceFormat aFormat, const IntRect& aBounds,
1133 TextureHandle* aHandle) {
1134 MOZ_ASSERT(aFormat == SurfaceFormat::B8G8R8A8 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat
::B8G8R8X8)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(aFormat == SurfaceFormat::B8G8R8A8 ||
aFormat == SurfaceFormat::B8G8R8X8))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::B8G8R8X8"
, "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 1135)
; AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::B8G8R8X8"
")"); do { MOZ_CrashSequence(__null, 1135); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1135 aFormat == SurfaceFormat::B8G8R8X8)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat
::B8G8R8X8)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(aFormat == SurfaceFormat::B8G8R8A8 ||
aFormat == SurfaceFormat::B8G8R8X8))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::B8G8R8X8"
, "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 1135)
; AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::B8G8R8X8"
")"); do { MOZ_CrashSequence(__null, 1135); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1136
1137 // If reading into a new texture, we have to bind it to a scratch framebuffer
1138 // for reading.
1139 if (aHandle) {
1140 if (!mScratchFramebuffer) {
1141 mScratchFramebuffer = mWebgl->CreateFramebuffer();
1142 }
1143 mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, mScratchFramebuffer);
1144 webgl::FbAttachInfo attachInfo;
1145 attachInfo.tex = aHandle->GetBackingTexture()->GetWebGLTexture();
1146 mWebgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER0x8D40, LOCAL_GL_COLOR_ATTACHMENT00x8CE0,
1147 LOCAL_GL_TEXTURE_2D0x0DE1, attachInfo);
1148 } else if (mCurrentTarget && mCurrentTarget->mIsClear) {
1149 // If reading from a target that is still clear, then avoid the readback by
1150 // just clearing the data.
1151 SkPixmap(MakeSkiaImageInfo(aBounds.Size(), aFormat), aDstData, aDstStride)
1152 .erase(IsOpaque(aFormat) ? SK_ColorBLACK : SK_ColorTRANSPARENT);
1153 return true;
1154 }
1155
1156 webgl::ReadPixelsDesc desc;
1157 desc.srcOffset = *ivec2::From(aBounds);
1158 desc.size = *uvec2::FromSize(aBounds);
1159 desc.packState.rowLength = aDstStride / 4;
1160 Range<uint8_t> range = {aDstData, size_t(aDstStride) * aBounds.height};
1161 mWebgl->ReadPixelsInto(desc, range);
1162
1163 // Restore the actual framebuffer after reading is done.
1164 if (aHandle && mCurrentTarget) {
1165 mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, mCurrentTarget->mFramebuffer);
1166 }
1167
1168 return true;
1169}
1170
1171already_AddRefed<DataSourceSurface> SharedContextWebgl::ReadSnapshot(
1172 TextureHandle* aHandle) {
1173 // Allocate a data surface, map it, and read from the WebGL context into the
1174 // surface.
1175 SurfaceFormat format = SurfaceFormat::UNKNOWN;
1176 IntRect bounds;
1177 if (aHandle) {
1178 format = aHandle->GetFormat();
1179 bounds = aHandle->GetBounds();
1180 } else {
1181 format = mCurrentTarget->GetFormat();
1182 bounds = mCurrentTarget->GetRect();
1183 }
1184 RefPtr<DataSourceSurface> surface =
1185 Factory::CreateDataSourceSurface(bounds.Size(), format);
1186 if (!surface) {
1187 return nullptr;
1188 }
1189 DataSourceSurface::ScopedMap dstMap(surface, DataSourceSurface::WRITE);
1190 if (!dstMap.IsMapped() || !ReadInto(dstMap.GetData(), dstMap.GetStride(),
1191 format, bounds, aHandle)) {
1192 return nullptr;
1193 }
1194 return surface.forget();
1195}
1196
1197// Utility method to install the target before reading a snapshot.
1198bool DrawTargetWebgl::ReadInto(uint8_t* aDstData, int32_t aDstStride) {
1199 if (!PrepareContext(false)) {
1200 return false;
1201 }
1202
1203 return mSharedContext->ReadInto(aDstData, aDstStride, GetFormat(), GetRect());
1204}
1205
1206// Utility method to install the target before reading a snapshot.
1207already_AddRefed<DataSourceSurface> DrawTargetWebgl::ReadSnapshot() {
1208 AutoRestoreContext restore(this);
1209 if (!PrepareContext(false)) {
1210 return nullptr;
1211 }
1212 mProfile.OnReadback();
1213 return mSharedContext->ReadSnapshot();
1214}
1215
1216already_AddRefed<SourceSurface> DrawTargetWebgl::GetBackingSurface() {
1217 return Snapshot();
1218}
1219
1220void DrawTargetWebgl::DetachAllSnapshots() {
1221 mSkia->DetachAllSnapshots();
1222 ClearSnapshot();
1223}
1224
1225// Prepare the framebuffer for accelerated drawing. Any cached snapshots will
1226// be invalidated if not detached and copied here. Ensure the WebGL
1227// framebuffer's contents are updated if still somehow stored in the Skia
1228// framebuffer.
1229bool DrawTargetWebgl::MarkChanged() {
1230 if (mSnapshot) {
1231 // Try to copy the target into a new texture if possible.
1232 ClearSnapshot(true, true);
1233 }
1234 if (!mWebglValid && !FlushFromSkia()) {
1235 return false;
1236 }
1237 mSkiaValid = false;
1238 mIsClear = false;
1239 return true;
1240}
1241
1242void DrawTargetWebgl::MarkSkiaChanged(bool aOverwrite) {
1243 if (aOverwrite) {
1244 mSkiaValid = true;
1245 mSkiaLayer = false;
1246 } else if (!mSkiaValid) {
1247 if (ReadIntoSkia()) {
1248 // Signal that we've hit a complete software fallback.
1249 mProfile.OnFallback();
1250 }
1251 } else if (mSkiaLayer && !mLayerDepth) {
1252 FlattenSkia();
1253 }
1254 mWebglValid = false;
1255 mIsClear = false;
1256}
1257
1258// Whether a given composition operator is associative and thus allows drawing
1259// into a separate layer that can be later composited back into the WebGL
1260// context.
1261static inline bool SupportsLayering(const DrawOptions& aOptions) {
1262 switch (aOptions.mCompositionOp) {
1263 case CompositionOp::OP_OVER:
1264 // Layering is only supported for the default source-over composition op.
1265 return true;
1266 default:
1267 return false;
1268 }
1269}
1270
1271void DrawTargetWebgl::MarkSkiaChanged(const DrawOptions& aOptions) {
1272 if (SupportsLayering(aOptions)) {
1273 if (!mSkiaValid) {
1274 // If the Skia context needs initialization, clear it and enable layering.
1275 mSkiaValid = true;
1276 if (mWebglValid) {
1277 mProfile.OnLayer();
1278 mSkiaLayer = true;
1279 mSkiaLayerClear = mIsClear;
1280 mSkia->DetachAllSnapshots();
1281 if (mSkiaLayerClear) {
1282 // Avoid blending later by making sure the layer background is filled
1283 // with opaque alpha values if necessary.
1284 mSkiaNoClip->FillRect(Rect(mSkiaNoClip->GetRect()), GetClearPattern(),
1285 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
1286 } else {
1287 mSkiaNoClip->ClearRect(Rect(mSkiaNoClip->GetRect()));
1288 }
1289 }
1290 }
1291 // The WebGL context is no longer up-to-date.
1292 mWebglValid = false;
1293 mIsClear = false;
1294 } else {
1295 // For other composition ops, just overwrite the Skia data.
1296 MarkSkiaChanged();
1297 }
1298}
1299
1300bool DrawTargetWebgl::LockBits(uint8_t** aData, IntSize* aSize,
1301 int32_t* aStride, SurfaceFormat* aFormat,
1302 IntPoint* aOrigin) {
1303 // Can only access pixels if there is valid, flattened Skia data.
1304 if (mSkiaValid && !mSkiaLayer) {
1305 MarkSkiaChanged();
1306 return mSkia->LockBits(aData, aSize, aStride, aFormat, aOrigin);
1307 }
1308 return false;
1309}
1310
1311void DrawTargetWebgl::ReleaseBits(uint8_t* aData) {
1312 // Can only access pixels if there is valid, flattened Skia data.
1313 if (mSkiaValid && !mSkiaLayer) {
1314 mSkia->ReleaseBits(aData);
1315 }
1316}
1317
1318// Format is x, y, alpha
1319static const float kRectVertexData[12] = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1320 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f};
1321
1322// Orphans the contents of the path vertex buffer. The beginning of the buffer
1323// always contains data for a simple rectangle draw to avoid needing to switch
1324// buffers.
1325void SharedContextWebgl::ResetPathVertexBuffer() {
1326 if (!mPathVertexBuffer) {
1327 MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")")
; do { MOZ_CrashSequence(__null, 1327); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1328 return;
1329 }
1330
1331 size_t oldCapacity = mPathVertexBuffer->ByteLength();
1332 RemoveUntrackedTextureMemory(oldCapacity);
1333
1334 mWebgl->BindBuffer(LOCAL_GL_ARRAY_BUFFER0x8892, mPathVertexBuffer.get());
1335 mWebgl->UninitializedBufferData_SizeOnly(
1336 LOCAL_GL_ARRAY_BUFFER0x8892,
1337 std::max(size_t(mPathVertexCapacity), sizeof(kRectVertexData)),
1338 LOCAL_GL_DYNAMIC_DRAW0x88E8);
1339 mWebgl->BufferSubData(LOCAL_GL_ARRAY_BUFFER0x8892, 0, sizeof(kRectVertexData),
1340 (const uint8_t*)kRectVertexData);
1341 mPathVertexOffset = sizeof(kRectVertexData);
1342
1343 size_t newCapacity = mPathVertexBuffer->ByteLength();
1344 AddUntrackedTextureMemory(newCapacity);
1345
1346 if (mWGROutputBuffer &&
1347 (!mPathVertexCapacity || newCapacity != oldCapacity)) {
1348 RemoveHeapData(mWGROutputBuffer.get());
1349 mWGROutputBuffer = nullptr;
1350 }
1351
1352 if (mPathVertexCapacity > 0 && !mWGROutputBuffer) {
1353 mWGROutputBuffer.reset(new (
1354 fallible) WGR::OutputVertex[newCapacity / sizeof(WGR::OutputVertex)]);
1355 AddHeapData(mWGROutputBuffer.get());
1356 }
1357}
1358
1359// Attempts to create all shaders and resources to be used for drawing commands.
1360// Returns whether or not this succeeded.
1361bool SharedContextWebgl::CreateShaders() {
1362 if (!mPathVertexArray) {
1363 mPathVertexArray = mWebgl->CreateVertexArray();
1364 }
1365 if (!mPathVertexBuffer) {
1366 mPathVertexBuffer = mWebgl->CreateBuffer();
1367 AddUntrackedTextureMemory(mPathVertexBuffer);
1368 mWebgl->BindVertexArray(mPathVertexArray.get());
1369 ResetPathVertexBuffer();
1370 mWebgl->EnableVertexAttribArray(0);
1371
1372 webgl::VertAttribPointerDesc attribDesc;
1373 attribDesc.channels = 3;
1374 attribDesc.type = LOCAL_GL_FLOAT0x1406;
1375 attribDesc.normalized = false;
1376 mWebgl->VertexAttribPointer(0, attribDesc);
1377 }
1378 if (!mSolidProgram) {
1379 // AA is computed by using the basis vectors of the transform to determine
1380 // both the scale and orientation. The scale is then used to extrude the
1381 // rectangle outward by 1 screen-space pixel to account for the AA region.
1382 // The distance to the rectangle edges is passed to the fragment shader in
1383 // an interpolant, biased by 0.5 so it represents the desired coverage. The
1384 // minimum coverage is then chosen by the fragment shader to use as an AA
1385 // coverage value to modulate the color.
1386 auto vsSource =
1387 "attribute vec3 a_vertex;\n"
1388 "uniform vec2 u_transform[3];\n"
1389 "uniform vec2 u_viewport;\n"
1390 "uniform vec4 u_clipbounds;\n"
1391 "uniform float u_aa;\n"
1392 "varying vec2 v_cliptc;\n"
1393 "varying vec4 v_clipdist;\n"
1394 "varying vec4 v_dist;\n"
1395 "varying float v_alpha;\n"
1396 "void main() {\n"
1397 " vec2 scale = vec2(dot(u_transform[0], u_transform[0]),\n"
1398 " dot(u_transform[1], u_transform[1]));\n"
1399 " vec2 invScale = u_aa * inversesqrt(scale + 1.0e-6);\n"
1400 " scale *= invScale;\n"
1401 " vec2 extrude = a_vertex.xy +\n"
1402 " invScale * (2.0 * a_vertex.xy - 1.0);\n"
1403 " vec2 vertex = u_transform[0] * extrude.x +\n"
1404 " u_transform[1] * extrude.y +\n"
1405 " u_transform[2];\n"
1406 " gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
1407 " v_cliptc = vertex / u_viewport;\n"
1408 " v_clipdist = vec4(vertex - u_clipbounds.xy,\n"
1409 " u_clipbounds.zw - vertex);\n"
1410 " float noAA = 1.0 - u_aa;\n"
1411 " v_dist = vec4(extrude, 1.0 - extrude) * scale.xyxy + 0.5 + noAA;\n"
1412 " v_alpha = min(a_vertex.z,\n"
1413 " min(scale.x, 1.0) * min(scale.y, 1.0) + noAA);\n"
1414 "}\n";
1415 auto fsSource =
1416 "precision mediump float;\n"
1417 "uniform vec4 u_color;\n"
1418 "uniform sampler2D u_clipmask;\n"
1419 "varying highp vec2 v_cliptc;\n"
1420 "varying vec4 v_clipdist;\n"
1421 "varying vec4 v_dist;\n"
1422 "varying float v_alpha;\n"
1423 "void main() {\n"
1424 " float clip = texture2D(u_clipmask, v_cliptc).r;\n"
1425 " vec4 dist = min(v_dist, v_clipdist);\n"
1426 " dist.xy = min(dist.xy, dist.zw);\n"
1427 " float aa = clamp(min(dist.x, dist.y), 0.0, v_alpha);\n"
1428 " gl_FragColor = clip * aa * u_color;\n"
1429 "}\n";
1430 RefPtr<WebGLShader> vsId = mWebgl->CreateShader(LOCAL_GL_VERTEX_SHADER0x8B31);
1431 mWebgl->ShaderSource(*vsId, vsSource);
1432 mWebgl->CompileShader(*vsId);
1433 if (!mWebgl->GetCompileResult(*vsId).success) {
1434 return false;
1435 }
1436 RefPtr<WebGLShader> fsId = mWebgl->CreateShader(LOCAL_GL_FRAGMENT_SHADER0x8B30);
1437 mWebgl->ShaderSource(*fsId, fsSource);
1438 mWebgl->CompileShader(*fsId);
1439 if (!mWebgl->GetCompileResult(*fsId).success) {
1440 return false;
1441 }
1442 mSolidProgram = mWebgl->CreateProgram();
1443 mWebgl->AttachShader(*mSolidProgram, *vsId);
1444 mWebgl->AttachShader(*mSolidProgram, *fsId);
1445 mWebgl->BindAttribLocation(*mSolidProgram, 0, "a_vertex");
1446 mWebgl->LinkProgram(*mSolidProgram);
1447 if (!mWebgl->GetLinkResult(*mSolidProgram).success) {
1448 return false;
1449 }
1450 mSolidProgramViewport = GetUniformLocation(mSolidProgram, "u_viewport");
1451 mSolidProgramAA = GetUniformLocation(mSolidProgram, "u_aa");
1452 mSolidProgramTransform = GetUniformLocation(mSolidProgram, "u_transform");
1453 mSolidProgramColor = GetUniformLocation(mSolidProgram, "u_color");
1454 mSolidProgramClipMask = GetUniformLocation(mSolidProgram, "u_clipmask");
1455 mSolidProgramClipBounds = GetUniformLocation(mSolidProgram, "u_clipbounds");
1456 if (!mSolidProgramViewport || !mSolidProgramAA || !mSolidProgramTransform ||
1457 !mSolidProgramColor || !mSolidProgramClipMask ||
1458 !mSolidProgramClipBounds) {
1459 return false;
1460 }
1461 mWebgl->UseProgram(mSolidProgram);
1462 UniformData(LOCAL_GL_INT0x1404, mSolidProgramClipMask, Array<int32_t, 1>{1});
1463 }
1464
1465 if (!mImageProgram) {
1466 auto vsSource =
1467 "attribute vec3 a_vertex;\n"
1468 "uniform vec2 u_viewport;\n"
1469 "uniform vec4 u_clipbounds;\n"
1470 "uniform float u_aa;\n"
1471 "uniform vec2 u_transform[3];\n"
1472 "uniform vec2 u_texmatrix[3];\n"
1473 "varying vec2 v_cliptc;\n"
1474 "varying vec2 v_texcoord;\n"
1475 "varying vec4 v_clipdist;\n"
1476 "varying vec4 v_dist;\n"
1477 "varying float v_alpha;\n"
1478 "void main() {\n"
1479 " vec2 scale = vec2(dot(u_transform[0], u_transform[0]),\n"
1480 " dot(u_transform[1], u_transform[1]));\n"
1481 " vec2 invScale = u_aa * inversesqrt(scale + 1.0e-6);\n"
1482 " scale *= invScale;\n"
1483 " vec2 extrude = a_vertex.xy +\n"
1484 " invScale * (2.0 * a_vertex.xy - 1.0);\n"
1485 " vec2 vertex = u_transform[0] * extrude.x +\n"
1486 " u_transform[1] * extrude.y +\n"
1487 " u_transform[2];\n"
1488 " gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
1489 " v_cliptc = vertex / u_viewport;\n"
1490 " v_clipdist = vec4(vertex - u_clipbounds.xy,\n"
1491 " u_clipbounds.zw - vertex);\n"
1492 " v_texcoord = u_texmatrix[0] * extrude.x +\n"
1493 " u_texmatrix[1] * extrude.y +\n"
1494 " u_texmatrix[2];\n"
1495 " float noAA = 1.0 - u_aa;\n"
1496 " v_dist = vec4(extrude, 1.0 - extrude) * scale.xyxy + 0.5 + noAA;\n"
1497 " v_alpha = min(a_vertex.z,\n"
1498 " min(scale.x, 1.0) * min(scale.y, 1.0) + noAA);\n"
1499 "}\n";
1500 auto fsSource =
1501 "precision mediump float;\n"
1502 "uniform vec4 u_texbounds;\n"
1503 "uniform vec4 u_color;\n"
1504 "uniform float u_swizzle;\n"
1505 "uniform sampler2D u_sampler;\n"
1506 "uniform sampler2D u_clipmask;\n"
1507 "varying highp vec2 v_cliptc;\n"
1508 "varying highp vec2 v_texcoord;\n"
1509 "varying vec4 v_clipdist;\n"
1510 "varying vec4 v_dist;\n"
1511 "varying float v_alpha;\n"
1512 "void main() {\n"
1513 " highp vec2 tc = clamp(v_texcoord, u_texbounds.xy,\n"
1514 " u_texbounds.zw);\n"
1515 " vec4 image = texture2D(u_sampler, tc);\n"
1516 " float clip = texture2D(u_clipmask, v_cliptc).r;\n"
1517 " vec4 dist = min(v_dist, v_clipdist);\n"
1518 " dist.xy = min(dist.xy, dist.zw);\n"
1519 " float aa = clamp(min(dist.x, dist.y), 0.0, v_alpha);\n"
1520 " gl_FragColor = clip * aa * u_color *\n"
1521 " mix(image, image.rrrr, u_swizzle);\n"
1522 "}\n";
1523 RefPtr<WebGLShader> vsId = mWebgl->CreateShader(LOCAL_GL_VERTEX_SHADER0x8B31);
1524 mWebgl->ShaderSource(*vsId, vsSource);
1525 mWebgl->CompileShader(*vsId);
1526 if (!mWebgl->GetCompileResult(*vsId).success) {
1527 return false;
1528 }
1529 RefPtr<WebGLShader> fsId = mWebgl->CreateShader(LOCAL_GL_FRAGMENT_SHADER0x8B30);
1530 mWebgl->ShaderSource(*fsId, fsSource);
1531 mWebgl->CompileShader(*fsId);
1532 if (!mWebgl->GetCompileResult(*fsId).success) {
1533 return false;
1534 }
1535 mImageProgram = mWebgl->CreateProgram();
1536 mWebgl->AttachShader(*mImageProgram, *vsId);
1537 mWebgl->AttachShader(*mImageProgram, *fsId);
1538 mWebgl->BindAttribLocation(*mImageProgram, 0, "a_vertex");
1539 mWebgl->LinkProgram(*mImageProgram);
1540 if (!mWebgl->GetLinkResult(*mImageProgram).success) {
1541 return false;
1542 }
1543 mImageProgramViewport = GetUniformLocation(mImageProgram, "u_viewport");
1544 mImageProgramAA = GetUniformLocation(mImageProgram, "u_aa");
1545 mImageProgramTransform = GetUniformLocation(mImageProgram, "u_transform");
1546 mImageProgramTexMatrix = GetUniformLocation(mImageProgram, "u_texmatrix");
1547 mImageProgramTexBounds = GetUniformLocation(mImageProgram, "u_texbounds");
1548 mImageProgramSwizzle = GetUniformLocation(mImageProgram, "u_swizzle");
1549 mImageProgramColor = GetUniformLocation(mImageProgram, "u_color");
1550 mImageProgramSampler = GetUniformLocation(mImageProgram, "u_sampler");
1551 mImageProgramClipMask = GetUniformLocation(mImageProgram, "u_clipmask");
1552 mImageProgramClipBounds = GetUniformLocation(mImageProgram, "u_clipbounds");
1553 if (!mImageProgramViewport || !mImageProgramAA || !mImageProgramTransform ||
1554 !mImageProgramTexMatrix || !mImageProgramTexBounds ||
1555 !mImageProgramSwizzle || !mImageProgramColor || !mImageProgramSampler ||
1556 !mImageProgramClipMask || !mImageProgramClipBounds) {
1557 return false;
1558 }
1559 mWebgl->UseProgram(mImageProgram);
1560 UniformData(LOCAL_GL_INT0x1404, mImageProgramSampler, Array<int32_t, 1>{0});
1561 UniformData(LOCAL_GL_INT0x1404, mImageProgramClipMask, Array<int32_t, 1>{1});
1562 }
1563 return true;
1564}
1565
1566void SharedContextWebgl::EnableScissor(const IntRect& aRect) {
1567 // Only update scissor state if it actually changes.
1568 if (!mLastScissor.IsEqualEdges(aRect)) {
1569 mLastScissor = aRect;
1570 mWebgl->Scissor(aRect.x, aRect.y, aRect.width, aRect.height);
1571 }
1572 if (!mScissorEnabled) {
1573 mScissorEnabled = true;
1574 mWebgl->SetEnabled(LOCAL_GL_SCISSOR_TEST0x0C11, {}, true);
1575 }
1576}
1577
1578void SharedContextWebgl::DisableScissor() {
1579 if (mScissorEnabled) {
1580 mScissorEnabled = false;
1581 mWebgl->SetEnabled(LOCAL_GL_SCISSOR_TEST0x0C11, {}, false);
1582 }
1583}
1584
1585inline ColorPattern DrawTargetWebgl::GetClearPattern() const {
1586 return ColorPattern(
1587 DeviceColor(0.0f, 0.0f, 0.0f, IsOpaque(mFormat) ? 1.0f : 0.0f));
1588}
1589
1590template <typename R>
1591inline RectDouble DrawTargetWebgl::TransformDouble(const R& aRect) const {
1592 return MatrixDouble(mTransform).TransformBounds(WidenToDouble(aRect));
1593}
1594
1595// Check if the transformed rect clips to the viewport.
1596inline Maybe<Rect> DrawTargetWebgl::RectClippedToViewport(
1597 const RectDouble& aRect) const {
1598 if (!mTransform.PreservesAxisAlignedRectangles()) {
1599 return Nothing();
1600 }
1601
1602 return Some(NarrowToFloat(aRect.SafeIntersect(RectDouble(GetRect()))));
1603}
1604
1605// Ensure that the rect, after transform, is within reasonable precision limits
1606// such that when transformed and clipped in the shader it will not round bits
1607// from the mantissa in a way that will diverge in a noticeable way from path
1608// geometry calculated by the path fallback.
1609template <typename R>
1610static inline bool RectInsidePrecisionLimits(const R& aRect) {
1611 return R(-(1 << 20), -(1 << 20), 2 << 20, 2 << 20).Contains(aRect);
1612}
1613
1614void DrawTargetWebgl::ClearRect(const Rect& aRect) {
1615 if (mIsClear) {
1616 // No need to clear anything if the entire framebuffer is already clear.
1617 return;
1618 }
1619
1620 RectDouble xformRect = TransformDouble(aRect);
1621 bool containsViewport = false;
1622 if (Maybe<Rect> clipped = RectClippedToViewport(xformRect)) {
1623 // If the rect clips to viewport, just clear the clipped rect
1624 // to avoid transform issues.
1625 containsViewport = clipped->Size() == Size(GetSize());
1626 DrawRect(*clipped, GetClearPattern(),
1627 DrawOptions(1.0f, CompositionOp::OP_CLEAR), Nothing(), nullptr,
1628 false);
1629 } else if (RectInsidePrecisionLimits(xformRect)) {
1630 // If the rect transform won't stress precision, then just use it.
1631 DrawRect(aRect, GetClearPattern(),
1632 DrawOptions(1.0f, CompositionOp::OP_CLEAR));
1633 } else {
1634 // Otherwise, using the transform in the shader may lead to inaccuracies, so
1635 // just fall back.
1636 MarkSkiaChanged();
1637 mSkia->ClearRect(aRect);
1638 }
1639
1640 // If the clear rectangle encompasses the entire viewport and is not clipped,
1641 // then mark the target as entirely clear.
1642 if (containsViewport && mSharedContext->IsCurrentTarget(this) &&
1643 !mSharedContext->HasClipMask() &&
1644 mSharedContext->mClipAARect.Contains(Rect(GetRect()))) {
1645 mIsClear = true;
1646 }
1647}
1648
1649static inline DeviceColor PremultiplyColor(const DeviceColor& aColor,
1650 float aAlpha = 1.0f) {
1651 float a = aColor.a * aAlpha;
1652 return DeviceColor(aColor.r * a, aColor.g * a, aColor.b * a, a);
1653}
1654
1655// Attempts to create the framebuffer used for drawing and also any relevant
1656// non-shared resources. Returns whether or not this succeeded.
1657bool DrawTargetWebgl::CreateFramebuffer() {
1658 RefPtr<WebGLContext> webgl = mSharedContext->mWebgl;
1659 if (!mFramebuffer) {
1660 mFramebuffer = webgl->CreateFramebuffer();
1661 }
1662 if (!mTex) {
1663 mTex = webgl->CreateTexture();
1664 webgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, mTex);
1665 webgl->TexStorage(LOCAL_GL_TEXTURE_2D0x0DE1, 1, LOCAL_GL_RGBA80x8058,
1666 {uint32_t(mSize.width), uint32_t(mSize.height), 1});
1667 mSharedContext->InitTexParameters(mTex);
1668 webgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, mFramebuffer);
1669 webgl::FbAttachInfo attachInfo;
1670 attachInfo.tex = mTex;
1671 webgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER0x8D40, LOCAL_GL_COLOR_ATTACHMENT00x8CE0,
1672 LOCAL_GL_TEXTURE_2D0x0DE1, attachInfo);
1673 webgl->Viewport(0, 0, mSize.width, mSize.height);
1674 mSharedContext->DisableScissor();
1675 DeviceColor color = PremultiplyColor(GetClearPattern().mColor);
1676 webgl->ClearColor(color.b, color.g, color.r, color.a);
1677 webgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT0x00004000);
1678 mSharedContext->ClearTarget();
1679 mSharedContext->ClearLastTexture();
1680 mSharedContext->AddUntrackedTextureMemory(mTex);
1681 }
1682 return true;
1683}
1684
1685void DrawTargetWebgl::CopySurface(SourceSurface* aSurface,
1686 const IntRect& aSourceRect,
1687 const IntPoint& aDestination) {
1688 // Intersect the source and destination rectangles with the viewport bounds.
1689 IntRect destRect =
1690 IntRect(aDestination, aSourceRect.Size()).SafeIntersect(GetRect());
1691 IntRect srcRect = destRect - aDestination + aSourceRect.TopLeft();
1692 if (srcRect.IsEmpty()) {
1693 return;
1694 }
1695
1696 if (mSkiaValid) {
1697 if (mSkiaLayer) {
1698 if (destRect.Contains(GetRect())) {
1699 // If the the destination would override the entire layer, discard the
1700 // layer.
1701 mSkiaLayer = false;
1702 } else if (!IsOpaque(aSurface->GetFormat())) {
1703 // If the surface is not opaque, copying it into the layer results in
1704 // unintended blending rather than a copy to the destination.
1705 FlattenSkia();
1706 }
1707 } else {
1708 // If there is no layer, copying is safe.
1709 MarkSkiaChanged();
1710 }
1711 mSkia->CopySurface(aSurface, srcRect, destRect.TopLeft());
1712 return;
1713 }
1714
1715 IntRect samplingRect;
1716 if (!mSharedContext->IsCompatibleSurface(aSurface)) {
1717 // If this data surface completely overwrites the framebuffer, then just
1718 // copy it to the Skia target.
1719 if (destRect.Contains(GetRect())) {
1720 MarkSkiaChanged(true);
1721 mSkia->DetachAllSnapshots();
1722 mSkiaNoClip->CopySurface(aSurface, srcRect, destRect.TopLeft());
1723 return;
1724 }
1725
1726 // CopySurface usually only samples a surface once, so don't cache the
1727 // entire surface as it is unlikely to be reused. Limit it to the used
1728 // source rectangle instead.
1729 IntRect surfaceRect = aSurface->GetRect();
1730 if (!srcRect.IsEqualEdges(surfaceRect)) {
1731 samplingRect = srcRect.SafeIntersect(surfaceRect) - surfaceRect.TopLeft();
1732 }
1733 }
1734
1735 Matrix matrix = Matrix::Translation(destRect.TopLeft() - srcRect.TopLeft());
1736 SurfacePattern pattern(aSurface, ExtendMode::CLAMP, matrix,
1737 SamplingFilter::POINT, samplingRect);
1738 DrawRect(Rect(destRect), pattern, DrawOptions(1.0f, CompositionOp::OP_SOURCE),
1739 Nothing(), nullptr, false, false);
1740}
1741
1742void DrawTargetWebgl::PushClip(const Path* aPath) {
1743 if (aPath && aPath->GetBackendType() == BackendType::SKIA) {
1744 // Detect if the path is really just a rect to simplify caching.
1745 if (Maybe<Rect> rect = aPath->AsRect()) {
1746 PushClipRect(*rect);
1747 return;
1748 }
1749 }
1750
1751 mClipChanged = true;
1752 mRefreshClipState = true;
1753 mSkia->PushClip(aPath);
1754
1755 mClipStack.push_back({GetTransform(), Rect(), aPath});
1756}
1757
1758void DrawTargetWebgl::PushClipRect(const Rect& aRect) {
1759 mClipChanged = true;
1760 mRefreshClipState = true;
1761 mSkia->PushClipRect(aRect);
1762
1763 mClipStack.push_back({GetTransform(), aRect, nullptr});
1764}
1765
1766void DrawTargetWebgl::PushDeviceSpaceClipRects(const IntRect* aRects,
1767 uint32_t aCount) {
1768 mClipChanged = true;
1769 mRefreshClipState = true;
1770 mSkia->PushDeviceSpaceClipRects(aRects, aCount);
1771
1772 for (uint32_t i = 0; i < aCount; i++) {
1773 mClipStack.push_back({Matrix(), Rect(aRects[i]), nullptr});
1774 }
1775}
1776
1777void DrawTargetWebgl::PopClip() {
1778 mClipChanged = true;
1779 mRefreshClipState = true;
1780 mSkia->PopClip();
1781
1782 mClipStack.pop_back();
1783}
1784
1785bool DrawTargetWebgl::RemoveAllClips() {
1786 if (mClipStack.empty()) {
1787 return true;
1788 }
1789 if (!mSkia->RemoveAllClips()) {
1790 return false;
1791 }
1792 mClipChanged = true;
1793 mRefreshClipState = true;
1794 mClipStack.clear();
1795 return true;
1796}
1797
1798bool DrawTargetWebgl::CopyToFallback(DrawTarget* aDT) {
1799 aDT->RemoveAllClips();
1800 for (auto& clipStack : mClipStack) {
1801 aDT->SetTransform(clipStack.mTransform);
1802 if (clipStack.mPath) {
1803 aDT->PushClip(clipStack.mPath);
1804 } else {
1805 aDT->PushClipRect(clipStack.mRect);
1806 }
1807 }
1808 aDT->SetTransform(GetTransform());
1809
1810 // An existing data snapshot is required for fallback, as we have to avoid
1811 // trying to touch the WebGL context, which is assumed to be invalid and not
1812 // suitable for readback.
1813 if (HasDataSnapshot()) {
1814 if (RefPtr<SourceSurface> snapshot = Snapshot()) {
1815 aDT->CopySurface(snapshot, snapshot->GetRect(), gfx::IntPoint(0, 0));
1816 return true;
1817 }
1818 }
1819 return false;
1820}
1821
1822// Whether a given composition operator can be mapped to a WebGL blend mode.
1823static inline bool SupportsDrawOptions(const DrawOptions& aOptions) {
1824 switch (aOptions.mCompositionOp) {
1825 case CompositionOp::OP_OVER:
1826 case CompositionOp::OP_ADD:
1827 case CompositionOp::OP_ATOP:
1828 case CompositionOp::OP_SOURCE:
1829 case CompositionOp::OP_CLEAR:
1830 return true;
1831 default:
1832 return false;
1833 }
1834}
1835
1836static inline bool SupportsExtendMode(const SurfacePattern& aPattern) {
1837 switch (aPattern.mExtendMode) {
1838 case ExtendMode::CLAMP:
1839 return true;
1840 case ExtendMode::REPEAT:
1841 case ExtendMode::REPEAT_X:
1842 case ExtendMode::REPEAT_Y:
1843 if ((!aPattern.mSurface ||
1844 aPattern.mSurface->GetUnderlyingType() == SurfaceType::WEBGL) &&
1845 !aPattern.mSamplingRect.IsEmpty()) {
1846 return false;
1847 }
1848 return true;
1849 default:
1850 return false;
1851 }
1852}
1853
1854// Whether a pattern can be mapped to an available WebGL shader.
1855bool SharedContextWebgl::SupportsPattern(const Pattern& aPattern) {
1856 switch (aPattern.GetType()) {
1857 case PatternType::COLOR:
1858 return true;
1859 case PatternType::SURFACE: {
1860 auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
1861 if (!SupportsExtendMode(surfacePattern)) {
1862 return false;
1863 }
1864 if (surfacePattern.mSurface) {
1865 // If the surface is already uploaded to a texture, then just use it.
1866 if (IsCompatibleSurface(surfacePattern.mSurface)) {
1867 return true;
1868 }
1869
1870 IntSize size = surfacePattern.mSurface->GetSize();
1871 // The maximum size a surface can be before triggering a fallback to
1872 // software. Bound the maximum surface size by the actual texture size
1873 // limit.
1874 int32_t maxSize = int32_t(
1875 std::min(StaticPrefs::gfx_canvas_accelerated_max_surface_size(),
1876 mMaxTextureSize));
1877 // Check if either of the surface dimensions or the sampling rect,
1878 // if supplied, exceed the maximum.
1879 if (std::max(size.width, size.height) > maxSize &&
1880 (surfacePattern.mSamplingRect.IsEmpty() ||
1881 std::max(surfacePattern.mSamplingRect.width,
1882 surfacePattern.mSamplingRect.height) > maxSize)) {
1883 return false;
1884 }
1885 }
1886 return true;
1887 }
1888 default:
1889 // Patterns other than colors and surfaces are currently not accelerated.
1890 return false;
1891 }
1892}
1893
1894bool DrawTargetWebgl::DrawRect(const Rect& aRect, const Pattern& aPattern,
1895 const DrawOptions& aOptions,
1896 Maybe<DeviceColor> aMaskColor,
1897 RefPtr<TextureHandle>* aHandle,
1898 bool aTransformed, bool aClipped,
1899 bool aAccelOnly, bool aForceUpdate,
1900 const StrokeOptions* aStrokeOptions) {
1901 // If there is nothing to draw, then don't draw...
1902 if (aRect.IsEmpty()) {
1903 return true;
1904 }
1905
1906 // If we're already drawing directly to the WebGL context, then we want to
1907 // continue to do so. However, if we're drawing into a Skia layer over the
1908 // WebGL context, then we need to be careful to avoid repeatedly clearing
1909 // and flushing the layer if we hit a drawing request that can be accelerated
1910 // in between layered drawing requests, as clearing and flushing the layer
1911 // can be significantly expensive when repeated. So when a Skia layer is
1912 // active, if it is possible to continue drawing into the layer, then don't
1913 // accelerate the drawing request.
1914 if (mWebglValid || (mSkiaLayer && !mLayerDepth &&
1915 (aAccelOnly || !SupportsLayering(aOptions)))) {
1916 // If we get here, either the WebGL context is being directly drawn to
1917 // or we are going to flush the Skia layer to it before doing so. The shared
1918 // context still needs to be claimed and prepared for drawing. If this
1919 // fails, we just fall back to drawing with Skia below.
1920 if (PrepareContext(aClipped)) {
1921 // The shared context is claimed and the framebuffer is now valid, so try
1922 // accelerated drawing.
1923 return mSharedContext->DrawRectAccel(
1924 aRect, aPattern, aOptions, aMaskColor, aHandle, aTransformed,
1925 aClipped, aAccelOnly, aForceUpdate, aStrokeOptions);
1926 }
1927 }
1928
1929 // Either there is no valid WebGL target to draw into, or we failed to prepare
1930 // it for drawing. The only thing we can do at this point is fall back to
1931 // drawing with Skia. If the request explicitly requires accelerated drawing,
1932 // then draw nothing before returning failure.
1933 if (!aAccelOnly) {
1934 DrawRectFallback(aRect, aPattern, aOptions, aMaskColor, aTransformed,
1935 aClipped, aStrokeOptions);
1936 }
1937 return false;
1938}
1939
1940void DrawTargetWebgl::DrawRectFallback(const Rect& aRect,
1941 const Pattern& aPattern,
1942 const DrawOptions& aOptions,
1943 Maybe<DeviceColor> aMaskColor,
1944 bool aTransformed, bool aClipped,
1945 const StrokeOptions* aStrokeOptions) {
1946 // Invalidate the WebGL target and prepare the Skia target for drawing.
1947 MarkSkiaChanged(aOptions);
1948
1949 if (aTransformed) {
1950 // If transforms are requested, then just translate back to FillRect.
1951 if (aMaskColor) {
1952 mSkia->Mask(ColorPattern(*aMaskColor), aPattern, aOptions);
1953 } else if (aStrokeOptions) {
1954 mSkia->StrokeRect(aRect, aPattern, *aStrokeOptions, aOptions);
1955 } else {
1956 mSkia->FillRect(aRect, aPattern, aOptions);
1957 }
1958 } else if (aClipped) {
1959 // If no transform was requested but clipping is still required, then
1960 // temporarily reset the transform before translating to FillRect.
1961 mSkia->SetTransform(Matrix());
1962 if (aMaskColor) {
1963 auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
1964 if (surfacePattern.mSamplingRect.IsEmpty()) {
1965 mSkia->MaskSurface(ColorPattern(*aMaskColor), surfacePattern.mSurface,
1966 aRect.TopLeft(), aOptions);
1967 } else {
1968 mSkia->Mask(ColorPattern(*aMaskColor), aPattern, aOptions);
1969 }
1970 } else if (aStrokeOptions) {
1971 mSkia->StrokeRect(aRect, aPattern, *aStrokeOptions, aOptions);
1972 } else {
1973 mSkia->FillRect(aRect, aPattern, aOptions);
1974 }
1975 mSkia->SetTransform(mTransform);
1976 } else if (aPattern.GetType() == PatternType::SURFACE) {
1977 // No transform nor clipping was requested, so it is essentially just a
1978 // copy.
1979 auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
1980 IntRect destRect = RoundedOut(aRect);
1981 IntRect srcRect =
1982 destRect - IntPoint::Round(surfacePattern.mMatrix.GetTranslation());
1983 mSkia->CopySurface(surfacePattern.mSurface, srcRect, destRect.TopLeft());
1984 } else {
1985 MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 1985); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")")
; do { MOZ_CrashSequence(__null, 1985); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1986 }
1987}
1988
1989inline already_AddRefed<WebGLTexture> SharedContextWebgl::GetCompatibleSnapshot(
1990 SourceSurface* aSurface) const {
1991 if (aSurface->GetUnderlyingType() == SurfaceType::WEBGL) {
1992 RefPtr<SourceSurfaceWebgl> webglSurf =
1993 aSurface->GetUnderlyingSurface().downcast<SourceSurfaceWebgl>();
1994 if (this == webglSurf->mSharedContext) {
1995 // If there is a snapshot copy in a texture handle, use that.
1996 if (webglSurf->mHandle) {
1997 return do_AddRef(
1998 webglSurf->mHandle->GetBackingTexture()->GetWebGLTexture());
1999 }
2000 if (RefPtr<DrawTargetWebgl> webglDT = webglSurf->GetTarget()) {
2001 // If there is a copy-on-write reference to a target, use its backing
2002 // texture directly. This is only safe if the targets don't match, but
2003 // MarkChanged should ensure that any snapshots were copied into a
2004 // texture handle before we ever get here.
2005 if (!IsCurrentTarget(webglDT)) {
2006 return do_AddRef(webglDT->mTex);
2007 }
2008 }
2009 }
2010 }
2011 return nullptr;
2012}
2013
2014inline bool SharedContextWebgl::IsCompatibleSurface(
2015 SourceSurface* aSurface) const {
2016 return bool(RefPtr<WebGLTexture>(GetCompatibleSnapshot(aSurface)));
2017}
2018
2019bool SharedContextWebgl::UploadSurface(DataSourceSurface* aData,
2020 SurfaceFormat aFormat,
2021 const IntRect& aSrcRect,
2022 const IntPoint& aDstOffset, bool aInit,
2023 bool aZero,
2024 const RefPtr<WebGLTexture>& aTex) {
2025 webgl::TexUnpackBlobDesc texDesc = {
2026 LOCAL_GL_TEXTURE_2D0x0DE1,
2027 {uint32_t(aSrcRect.width), uint32_t(aSrcRect.height), 1}};
2028 if (aData) {
2029 // The surface needs to be uploaded to its backing texture either to
2030 // initialize or update the texture handle contents. Map the data
2031 // contents of the surface so it can be read.
2032 DataSourceSurface::ScopedMap map(aData, DataSourceSurface::READ);
2033 if (!map.IsMapped()) {
2034 return false;
2035 }
2036 int32_t stride = map.GetStride();
2037 int32_t bpp = BytesPerPixel(aFormat);
2038 // Get the data pointer range considering the sampling rect offset and
2039 // size.
2040 Span<const uint8_t> range(
2041 map.GetData() + aSrcRect.y * size_t(stride) + aSrcRect.x * bpp,
2042 std::max(aSrcRect.height - 1, 0) * size_t(stride) +
2043 aSrcRect.width * bpp);
2044 texDesc.cpuData = Some(range);
2045 // If the stride happens to be 4 byte aligned, assume that is the
2046 // desired alignment regardless of format (even A8). Otherwise, we
2047 // default to byte alignment.
2048 texDesc.unpacking.alignmentInTypeElems = stride % 4 ? 1 : 4;
2049 texDesc.unpacking.rowLength = stride / bpp;
2050 } else if (aZero) {
2051 // Create a PBO filled with zero data to initialize the texture data and
2052 // avoid slow initialization inside WebGL.
2053 MOZ_ASSERT(aSrcRect.TopLeft() == IntPoint(0, 0))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSrcRect.TopLeft() == IntPoint(0, 0))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSrcRect.TopLeft() == IntPoint
(0, 0)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aSrcRect.TopLeft() == IntPoint(0, 0)", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 2053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSrcRect.TopLeft() == IntPoint(0, 0)"
")"); do { MOZ_CrashSequence(__null, 2053); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2054 size_t size =
2055 size_t(GetAlignedStride<4>(aSrcRect.width, BytesPerPixel(aFormat))) *
2056 aSrcRect.height;
2057 if (!mZeroBuffer || size > mZeroSize) {
2058 ClearZeroBuffer();
2059 mZeroBuffer = mWebgl->CreateBuffer();
2060 mZeroSize = size;
2061 mWebgl->BindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER0x88EC, mZeroBuffer);
2062 // WebGL will zero initialize the empty buffer, so we don't send zero data
2063 // explicitly.
2064 mWebgl->BufferData(LOCAL_GL_PIXEL_UNPACK_BUFFER0x88EC, size, nullptr,
2065 LOCAL_GL_STATIC_DRAW0x88E4);
2066 AddUntrackedTextureMemory(mZeroBuffer);
2067 } else {
2068 mWebgl->BindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER0x88EC, mZeroBuffer);
2069 }
2070 texDesc.pboOffset = Some(0);
2071 }
2072 // Upload as RGBA8 to avoid swizzling during upload. Surfaces provide
2073 // data as BGRA, but we manually swizzle that in the shader. An A8
2074 // surface will be stored as an R8 texture that will also be swizzled
2075 // in the shader.
2076 GLenum intFormat =
2077 aFormat == SurfaceFormat::A8 ? LOCAL_GL_R80x8229 : LOCAL_GL_RGBA80x8058;
2078 GLenum extFormat =
2079 aFormat == SurfaceFormat::A8 ? LOCAL_GL_RED0x1903 : LOCAL_GL_RGBA0x1908;
2080 webgl::PackingInfo texPI = {extFormat, LOCAL_GL_UNSIGNED_BYTE0x1401};
2081 // Do the (partial) upload for the shared or standalone texture.
2082 if (aTex) {
2083 mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, aTex);
2084 }
2085 mWebgl->TexImage(0, aInit ? intFormat : 0,
2086 {uint32_t(aDstOffset.x), uint32_t(aDstOffset.y), 0}, texPI,
2087 texDesc);
2088 if (aTex) {
2089 mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, mLastTexture);
2090 }
2091 if (!aData && aZero) {
2092 mWebgl->BindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER0x88EC, 0);
2093 }
2094 return true;
2095}
2096
2097// Allocate a new texture handle backed by either a standalone texture or as a
2098// sub-texture of a larger shared texture.
2099already_AddRefed<TextureHandle> SharedContextWebgl::AllocateTextureHandle(
2100 SurfaceFormat aFormat, const IntSize& aSize, bool aAllowShared,
2101 bool aRenderable) {
2102 RefPtr<TextureHandle> handle;
2103 // Calculate the bytes that would be used by this texture handle, and prune
2104 // enough other textures to ensure we have that much usable texture space
2105 // available to allocate.
2106 size_t usedBytes = BackingTexture::UsedBytes(aFormat, aSize);
2107 PruneTextureMemory(usedBytes, false);
2108 // The requested page size for shared textures.
2109 int32_t pageSize = int32_t(std::min(
2110 StaticPrefs::gfx_canvas_accelerated_shared_page_size(), mMaxTextureSize));
2111 if (aAllowShared && std::max(aSize.width, aSize.height) <= pageSize / 2) {
2112 // Ensure that the surface is no bigger than a quadrant of a shared texture
2113 // page. If so, try to allocate it to a shared texture. Look for any
2114 // existing shared texture page with a matching format and allocate
2115 // from that if possible.
2116 for (auto& shared : mSharedTextures) {
2117 if (shared->GetFormat() == aFormat &&
2118 shared->IsRenderable() == aRenderable) {
2119 bool wasEmpty = !shared->HasAllocatedHandles();
2120 handle = shared->Allocate(aSize);
2121 if (handle) {
2122 if (wasEmpty) {
2123 // If the page was previously empty, then deduct it from the
2124 // empty memory reserves.
2125 mEmptyTextureMemory -= shared->UsedBytes();
2126 }
2127 break;
2128 }
2129 }
2130 }
2131 // If we couldn't find an existing shared texture page with matching
2132 // format, then allocate a new page to put the request in.
2133 if (!handle) {
2134 if (RefPtr<WebGLTexture> tex = mWebgl->CreateTexture()) {
2135 RefPtr<SharedTexture> shared =
2136 new SharedTexture(IntSize(pageSize, pageSize), aFormat, tex);
2137 if (aRenderable) {
2138 shared->MarkRenderable();
2139 }
2140 mSharedTextures.push_back(shared);
2141 AddTextureMemory(shared);
2142 handle = shared->Allocate(aSize);
2143 }
2144 }
2145 } else {
2146 // The surface wouldn't fit in a shared texture page, so we need to
2147 // allocate a standalone texture for it instead.
2148 if (RefPtr<WebGLTexture> tex = mWebgl->CreateTexture()) {
2149 RefPtr<StandaloneTexture> standalone =
2150 new StandaloneTexture(aSize, aFormat, tex);
2151 if (aRenderable) {
2152 standalone->MarkRenderable();
2153 }
2154 mStandaloneTextures.push_back(standalone);
2155 AddTextureMemory(standalone);
2156 handle = standalone;
2157 }
2158 }
2159
2160 if (!handle) {
2161 return nullptr;
2162 }
2163
2164 // Insert the new texture handle into the front of the MRU list and
2165 // update used space for it.
2166 mTextureHandles.insertFront(handle);
2167 ++mNumTextureHandles;
2168 mUsedTextureMemory += handle->UsedBytes();
2169
2170 return handle.forget();
2171}
2172
2173static inline SamplingFilter GetSamplingFilter(const Pattern& aPattern) {
2174 return aPattern.GetType() == PatternType::SURFACE
2175 ? static_cast<const SurfacePattern&>(aPattern).mSamplingFilter
2176 : SamplingFilter::GOOD;
2177}
2178
2179static inline bool UseNearestFilter(const Pattern& aPattern) {
2180 return GetSamplingFilter(aPattern) == SamplingFilter::POINT;
2181}
2182
2183// Determine if the rectangle is still axis-aligned and pixel-aligned.
2184static inline Maybe<IntRect> IsAlignedRect(bool aTransformed,
2185 const Matrix& aCurrentTransform,
2186 const Rect& aRect) {
2187 if (!aTransformed || aCurrentTransform.HasOnlyIntegerTranslation()) {
2188 auto intRect = RoundedToInt(aRect);
2189 if (aRect.WithinEpsilonOf(Rect(intRect), 1.0e-3f)) {
2190 if (aTransformed) {
2191 intRect += RoundedToInt(aCurrentTransform.GetTranslation());
2192 }
2193 return Some(intRect);
2194 }
2195 }
2196 return Nothing();
2197}
2198
2199Maybe<uint32_t> SharedContextWebgl::GetUniformLocation(
2200 const RefPtr<WebGLProgram>& aProg, const std::string& aName) const {
2201 if (!aProg || !aProg->LinkInfo()) {
2202 return Nothing();
2203 }
2204
2205 for (const auto& activeUniform : aProg->LinkInfo()->active.activeUniforms) {
2206 if (activeUniform.block_index != -1) continue;
2207
2208 auto locName = activeUniform.name;
2209 const auto indexed = webgl::ParseIndexed(locName);
2210 if (indexed) {
2211 locName = indexed->name;
2212 }
2213
2214 const auto baseLength = locName.size();
2215 for (const auto& pair : activeUniform.locByIndex) {
2216 if (indexed) {
2217 locName.erase(baseLength); // Erase previous "[N]".
2218 locName += '[';
2219 locName += std::to_string(pair.first);
2220 locName += ']';
2221 }
2222 if (locName == aName || locName == aName + "[0]") {
2223 return Some(pair.second);
2224 }
2225 }
2226 }
2227
2228 return Nothing();
2229}
2230
2231template <class T>
2232struct IsUniformDataValT : std::false_type {};
2233template <>
2234struct IsUniformDataValT<webgl::UniformDataVal> : std::true_type {};
2235template <>
2236struct IsUniformDataValT<float> : std::true_type {};
2237template <>
2238struct IsUniformDataValT<int32_t> : std::true_type {};
2239template <>
2240struct IsUniformDataValT<uint32_t> : std::true_type {};
2241
2242template <typename T, typename = std::enable_if_t<IsUniformDataValT<T>::value>>
2243inline Range<const webgl::UniformDataVal> AsUniformDataVal(
2244 const Range<const T>& data) {
2245 return {data.begin().template ReinterpretCast<const webgl::UniformDataVal>(),
2246 data.end().template ReinterpretCast<const webgl::UniformDataVal>()};
2247}
2248
2249template <class T, size_t N>
2250inline void SharedContextWebgl::UniformData(GLenum aFuncElemType,
2251 const Maybe<uint32_t>& aLoc,
2252 const Array<T, N>& aData) {
2253 // We currently always pass false for transpose. If in the future we need
2254 // support for transpose then caching needs to take that in to account.
2255 mWebgl->UniformData(*aLoc, false,
2256 AsUniformDataVal(Range<const T>(Span<const T>(aData))));
2257}
2258
2259template <class T, size_t N>
2260void SharedContextWebgl::MaybeUniformData(GLenum aFuncElemType,
2261 const Maybe<uint32_t>& aLoc,
2262 const Array<T, N>& aData,
2263 Maybe<Array<T, N>>& aCached) {
2264 if (aCached.isNothing() || !(*aCached == aData)) {
2265 aCached = Some(aData);
2266 UniformData(aFuncElemType, aLoc, aData);
2267 }
2268}
2269
2270inline void SharedContextWebgl::DrawQuad() {
2271 mWebgl->DrawArraysInstanced(LOCAL_GL_TRIANGLE_FAN0x0006, 0, 4, 1);
2272}
2273
2274void SharedContextWebgl::DrawTriangles(const PathVertexRange& aRange) {
2275 mWebgl->DrawArraysInstanced(LOCAL_GL_TRIANGLES0x0004, GLint(aRange.mOffset),
2276 GLsizei(aRange.mLength), 1);
2277}
2278
2279// Common rectangle and pattern drawing function shared by many DrawTarget
2280// commands. If aMaskColor is specified, the provided surface pattern will be
2281// treated as a mask. If aHandle is specified, then the surface pattern's
2282// texture will be cached in the supplied handle, as opposed to using the
2283// surface's user data. If aTransformed or aClipped are false, then transforms
2284// and/or clipping will be disabled. If aAccelOnly is specified, then this
2285// function will return before it would have otherwise drawn without
2286// acceleration. If aForceUpdate is specified, then the provided texture handle
2287// will be respecified with the provided surface.
2288bool SharedContextWebgl::DrawRectAccel(
2289 const Rect& aRect, const Pattern& aPattern, const DrawOptions& aOptions,
2290 Maybe<DeviceColor> aMaskColor, RefPtr<TextureHandle>* aHandle,
2291 bool aTransformed, bool aClipped, bool aAccelOnly, bool aForceUpdate,
2292 const StrokeOptions* aStrokeOptions, const PathVertexRange* aVertexRange,
2293 const Matrix* aRectXform) {
2294 // If the rect or clip rect is empty, then there is nothing to draw.
2295 if (aRect.IsEmpty() || mClipRect.IsEmpty()) {
2296 return true;
2297 }
2298
2299 // Check if the drawing options and the pattern support acceleration. Also
2300 // ensure the framebuffer is prepared for drawing. If not, fall back to using
2301 // the Skia target. When we need to forcefully update a texture, we must be
2302 // careful to override any pattern limits, as the caller ensures the pattern
2303 // is otherwise a supported type.
2304 if (!SupportsDrawOptions(aOptions) ||
2305 (!aForceUpdate && !SupportsPattern(aPattern)) || aStrokeOptions ||
2306 !mCurrentTarget->MarkChanged()) {
2307 // If only accelerated drawing was requested, bail out without software
2308 // drawing fallback.
2309 if (!aAccelOnly) {
2310 MOZ_ASSERT(!aVertexRange)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aVertexRange)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aVertexRange))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!aVertexRange",
"/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 2310);
AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aVertexRange" ")")
; do { MOZ_CrashSequence(__null, 2310); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2311 mCurrentTarget->DrawRectFallback(aRect, aPattern, aOptions, aMaskColor,
2312 aTransformed, aClipped, aStrokeOptions);
2313 }
2314 return false;
2315 }
2316
2317 const Matrix& currentTransform = mCurrentTarget->GetTransform();
2318 // rectXform only applies to the rect, but should not apply to the pattern,
2319 // as it might inadvertently alter the pattern.
2320 Matrix rectXform = currentTransform;
2321 if (aRectXform) {
2322 rectXform.PreMultiply(*aRectXform);
2323 }
2324
2325 if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE && aTransformed &&
2326 aClipped &&
2327 (HasClipMask() || !rectXform.PreservesAxisAlignedRectangles() ||
2328 !rectXform.TransformBounds(aRect).Contains(Rect(mClipAARect)) ||
2329 (aPattern.GetType() == PatternType::SURFACE &&
2330 !IsAlignedRect(aTransformed, rectXform, aRect)))) {
2331 // Clear outside the mask region for masks that are not bounded by clip.
2332 return DrawRectAccel(Rect(mClipRect), ColorPattern(DeviceColor(0, 0, 0, 0)),
2333 DrawOptions(1.0f, CompositionOp::OP_SOURCE,
2334 aOptions.mAntialiasMode),
2335 Nothing(), nullptr, false, aClipped, aAccelOnly) &&
2336 DrawRectAccel(aRect, aPattern,
2337 DrawOptions(aOptions.mAlpha, CompositionOp::OP_ADD,
2338 aOptions.mAntialiasMode),
2339 aMaskColor, aHandle, aTransformed, aClipped,
2340 aAccelOnly, aForceUpdate, aStrokeOptions, aVertexRange,
2341 aRectXform);
2342 }
2343 if (aOptions.mCompositionOp == CompositionOp::OP_CLEAR &&
2344 aPattern.GetType() == PatternType::SURFACE && !aMaskColor) {
2345 // If the surface being drawn with clear is not a mask, then its contents
2346 // needs to be ignored. Just use a color pattern instead.
2347 return DrawRectAccel(aRect, ColorPattern(DeviceColor(1, 1, 1, 1)), aOptions,
2348 Nothing(), aHandle, aTransformed, aClipped, aAccelOnly,
2349 aForceUpdate, aStrokeOptions, aVertexRange,
2350 aRectXform);
2351 }
2352
2353 // Set up the scissor test to reflect the clipping rectangle, if supplied.
2354 if (!mClipRect.Contains(IntRect(IntPoint(), mViewportSize))) {
2355 EnableScissor(mClipRect);
2356 } else {
2357 DisableScissor();
2358 }
2359
2360 bool success = false;
2361
2362 // Now try to actually draw the pattern...
2363 switch (aPattern.GetType()) {
2364 case PatternType::COLOR: {
2365 if (!aVertexRange) {
2366 // Only an uncached draw if not using the vertex cache.
2367 mCurrentTarget->mProfile.OnUncachedDraw();
2368 }
2369 DeviceColor color = PremultiplyColor(
2370 static_cast<const ColorPattern&>(aPattern).mColor, aOptions.mAlpha);
2371 if (((color.a == 1.0f &&
2372 aOptions.mCompositionOp == CompositionOp::OP_OVER) ||
2373 aOptions.mCompositionOp == CompositionOp::OP_SOURCE ||
2374 aOptions.mCompositionOp == CompositionOp::OP_CLEAR) &&
2375 !aStrokeOptions && !aVertexRange && !HasClipMask() &&
2376 mClipAARect.IsEqualEdges(Rect(mClipRect))) {
2377 // Certain color patterns can be mapped to scissored clears. The
2378 // composition op must effectively overwrite the destination, and the
2379 // transform must map to an axis-aligned integer rectangle.
2380 if (Maybe<IntRect> intRect =
2381 IsAlignedRect(aTransformed, rectXform, aRect)) {
2382 // Only use a clear if the area is larger than a quarter or the
2383 // viewport.
2384 if (intRect->Area() >=
2385 (mViewportSize.width / 2) * (mViewportSize.height / 2)) {
2386 if (!intRect->Contains(mClipRect)) {
2387 EnableScissor(intRect->Intersect(mClipRect));
2388 }
2389 if (aOptions.mCompositionOp == CompositionOp::OP_CLEAR) {
2390 color =
2391 PremultiplyColor(mCurrentTarget->GetClearPattern().mColor);
2392 }
2393 mWebgl->ClearColor(color.b, color.g, color.r, color.a);
2394 mWebgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT0x00004000);
2395 success = true;
2396 break;
2397 }
2398 }
2399 }
2400 // Map the composition op to a WebGL blend mode, if possible.
2401 Maybe<DeviceColor> blendColor;
2402 if (aOptions.mCompositionOp == CompositionOp::OP_SOURCE ||
2403 aOptions.mCompositionOp == CompositionOp::OP_CLEAR) {
2404 // The source operator can support clipping and AA by emulating it with
2405 // the over op. Supply the color with blend state, and set the shader
2406 // color to white, to avoid needing dual-source blending.
2407 blendColor = Some(color);
2408 // Both source and clear operators should output a mask from the shader.
2409 color = DeviceColor(1, 1, 1, 1);
2410 }
2411 SetBlendState(aOptions.mCompositionOp, blendColor);
2412 // Since it couldn't be mapped to a scissored clear, we need to use the
2413 // solid color shader with supplied transform.
2414 if (mLastProgram != mSolidProgram) {
2415 mWebgl->UseProgram(mSolidProgram);
2416 mLastProgram = mSolidProgram;
2417 }
2418 Array<float, 2> viewportData = {float(mViewportSize.width),
2419 float(mViewportSize.height)};
2420 MaybeUniformData(LOCAL_GL_FLOAT_VEC20x8B50, mSolidProgramViewport, viewportData,
2421 mSolidProgramUniformState.mViewport);
2422
2423 // Generated paths provide their own AA as vertex alpha.
2424 Array<float, 1> aaData = {aVertexRange ? 0.0f : 1.0f};
2425 MaybeUniformData(LOCAL_GL_FLOAT0x1406, mSolidProgramAA, aaData,
2426 mSolidProgramUniformState.mAA);
2427
2428 // Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
2429 // boundary.
2430 Array<float, 4> clipData = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
2431 mClipAARect.XMost() + 0.5f,
2432 mClipAARect.YMost() + 0.5f};
2433 MaybeUniformData(LOCAL_GL_FLOAT_VEC40x8B52, mSolidProgramClipBounds, clipData,
2434 mSolidProgramUniformState.mClipBounds);
2435
2436 Array<float, 4> colorData = {color.b, color.g, color.r, color.a};
2437 Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
2438 if (aTransformed) {
2439 xform *= rectXform;
2440 }
2441 Array<float, 6> xformData = {xform._11, xform._12, xform._21,
2442 xform._22, xform._31, xform._32};
2443 MaybeUniformData(LOCAL_GL_FLOAT_VEC20x8B50, mSolidProgramTransform, xformData,
2444 mSolidProgramUniformState.mTransform);
2445
2446 MaybeUniformData(LOCAL_GL_FLOAT_VEC40x8B52, mSolidProgramColor, colorData,
2447 mSolidProgramUniformState.mColor);
2448
2449 // Finally draw the colored rectangle.
2450 if (aVertexRange) {
2451 // If there's a vertex range, then we need to draw triangles within from
2452 // generated from a path stored in the path vertex buffer.
2453 DrawTriangles(*aVertexRange);
2454 } else {
2455 // Otherwise we're drawing a simple filled rectangle.
2456 DrawQuad();
2457 }
2458 success = true;
2459 break;
2460 }
2461 case PatternType::SURFACE: {
2462 auto surfacePattern = static_cast<const SurfacePattern&>(aPattern);
2463 // If a texture handle was supplied, or if the surface already has an
2464 // assigned texture handle stashed in its used data, try to use it.
2465 RefPtr<SourceSurface> underlyingSurface =
2466 surfacePattern.mSurface
2467 ? surfacePattern.mSurface->GetUnderlyingSurface()
2468 : nullptr;
2469 RefPtr<TextureHandle> handle =
2470 aHandle
2471 ? aHandle->get()
2472 : (underlyingSurface
2473 ? static_cast<TextureHandle*>(
2474 underlyingSurface->GetUserData(&mTextureHandleKey))
2475 : nullptr);
2476 IntSize texSize;
2477 IntPoint offset;
2478 SurfaceFormat format;
2479 // Check if the found handle is still valid and if its sampling rect
2480 // matches the requested sampling rect.
2481 if (handle && handle->IsValid() &&
2482 (surfacePattern.mSamplingRect.IsEmpty() ||
2483 handle->GetSamplingRect().IsEqualEdges(
2484 surfacePattern.mSamplingRect)) &&
2485 (surfacePattern.mExtendMode == ExtendMode::CLAMP ||
2486 handle->GetType() == TextureHandle::STANDALONE)) {
2487 texSize = handle->GetSize();
2488 format = handle->GetFormat();
2489 offset = handle->GetSamplingOffset();
2490 } else {
2491 // Otherwise, there is no handle that can be used yet, so extract
2492 // information from the surface pattern.
2493 handle = nullptr;
2494 if (!underlyingSurface) {
2495 // If there was no actual surface supplied, then we tried to draw
2496 // using a texture handle, but the texture handle wasn't valid.
2497 break;
2498 }
2499 texSize = underlyingSurface->GetSize();
2500 format = underlyingSurface->GetFormat();
2501 if (!surfacePattern.mSamplingRect.IsEmpty()) {
2502 texSize = surfacePattern.mSamplingRect.Size();
2503 offset = surfacePattern.mSamplingRect.TopLeft();
2504 }
2505 }
2506
2507 // We need to be able to transform from local space into texture space.
2508 Matrix invMatrix = surfacePattern.mMatrix;
2509 // Determine if the requested surface itself is offset from the underlying
2510 // surface.
2511 if (surfacePattern.mSurface) {
2512 invMatrix.PreTranslate(surfacePattern.mSurface->GetRect().TopLeft());
2513 }
2514 // If drawing a pre-transformed vertex range, then we need to ensure the
2515 // user-space pattern is still transformed to screen-space.
2516 if (aVertexRange && !aTransformed) {
2517 invMatrix *= currentTransform;
2518 }
2519 if (!invMatrix.Invert()) {
2520 break;
2521 }
2522 if (aRectXform) {
2523 // If there is aRectXform, it must be applied to the source rectangle to
2524 // generate the proper input coordinates for the inverse pattern matrix.
2525 invMatrix.PreMultiply(*aRectXform);
2526 }
2527
2528 RefPtr<WebGLTexture> tex;
2529 IntRect bounds;
2530 IntSize backingSize;
2531 RefPtr<DataSourceSurface> data;
2532 if (handle) {
2533 if (aForceUpdate) {
2534 data = underlyingSurface->GetDataSurface();
2535 if (!data) {
2536 break;
2537 }
2538 // The size of the texture may change if we update contents.
2539 mUsedTextureMemory -= handle->UsedBytes();
2540 handle->UpdateSize(texSize);
2541 mUsedTextureMemory += handle->UsedBytes();
2542 handle->SetSamplingOffset(surfacePattern.mSamplingRect.TopLeft());
2543 }
2544 // If using an existing handle, move it to the front of the MRU list.
2545 handle->remove();
2546 mTextureHandles.insertFront(handle);
2547 } else if ((tex = GetCompatibleSnapshot(underlyingSurface))) {
2548 backingSize = underlyingSurface->GetSize();
2549 bounds = IntRect(offset, texSize);
2550 // Count reusing a snapshot texture (no readback) as a cache hit.
2551 mCurrentTarget->mProfile.OnCacheHit();
2552 } else {
2553 // If we get here, we need a data surface for a texture upload.
2554 data = underlyingSurface->GetDataSurface();
2555 if (!data) {
2556 break;
2557 }
2558 // There is no existing handle. Try to allocate a new one. If the
2559 // surface size may change via a forced update, then don't allocate
2560 // from a shared texture page.
2561 handle = AllocateTextureHandle(
2562 format, texSize,
2563 !aForceUpdate && surfacePattern.mExtendMode == ExtendMode::CLAMP);
2564 if (!handle) {
2565 MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 2565); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")")
; do { MOZ_CrashSequence(__null, 2565); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2566 break;
2567 }
2568 // Link the handle to the surface's user data.
2569 handle->SetSamplingOffset(surfacePattern.mSamplingRect.TopLeft());
2570 if (aHandle) {
2571 *aHandle = handle;
2572 } else {
2573 handle->SetSurface(underlyingSurface);
2574 underlyingSurface->AddUserData(&mTextureHandleKey, handle.get(),
2575 nullptr);
2576 }
2577 }
2578
2579 // Map the composition op to a WebGL blend mode, if possible. If there is
2580 // a mask color and a texture with multiple channels, assume subpixel
2581 // blending. If we encounter the source op here, then assume the surface
2582 // is opaque (non-opaque is handled above) and emulate it with over.
2583 SetBlendState(aOptions.mCompositionOp,
2584 format != SurfaceFormat::A8 ? aMaskColor : Nothing());
2585 // Switch to the image shader and set up relevant transforms.
2586 if (mLastProgram != mImageProgram) {
2587 mWebgl->UseProgram(mImageProgram);
2588 mLastProgram = mImageProgram;
2589 }
2590
2591 Array<float, 2> viewportData = {float(mViewportSize.width),
2592 float(mViewportSize.height)};
2593 MaybeUniformData(LOCAL_GL_FLOAT_VEC20x8B50, mImageProgramViewport, viewportData,
2594 mImageProgramUniformState.mViewport);
2595
2596 // AA is not supported for OP_SOURCE. Generated paths provide their own
2597 // AA as vertex alpha.
2598 Array<float, 1> aaData = {
2599 mLastCompositionOp == CompositionOp::OP_SOURCE || aVertexRange
2600 ? 0.0f
2601 : 1.0f};
2602 MaybeUniformData(LOCAL_GL_FLOAT0x1406, mImageProgramAA, aaData,
2603 mImageProgramUniformState.mAA);
2604
2605 // Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
2606 // boundary.
2607 Array<float, 4> clipData = {mClipAARect.x - 0.5f, mClipAARect.y - 0.5f,
2608 mClipAARect.XMost() + 0.5f,
2609 mClipAARect.YMost() + 0.5f};
2610 MaybeUniformData(LOCAL_GL_FLOAT_VEC40x8B52, mImageProgramClipBounds, clipData,
2611 mImageProgramUniformState.mClipBounds);
2612
2613 DeviceColor color =
2614 mLastCompositionOp == CompositionOp::OP_CLEAR
2615 ? DeviceColor(1, 1, 1, 1)
2616 : PremultiplyColor(
2617 aMaskColor && format != SurfaceFormat::A8
2618 ? DeviceColor::Mask(1.0f, aMaskColor->a)
2619 : aMaskColor.valueOr(DeviceColor(1, 1, 1, 1)),
2620 aOptions.mAlpha);
2621 Array<float, 4> colorData = {color.b, color.g, color.r, color.a};
2622 Array<float, 1> swizzleData = {format == SurfaceFormat::A8 ? 1.0f : 0.0f};
2623 Matrix xform(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
2624 if (aTransformed) {
2625 xform *= rectXform;
2626 }
2627 Array<float, 6> xformData = {xform._11, xform._12, xform._21,
2628 xform._22, xform._31, xform._32};
2629 MaybeUniformData(LOCAL_GL_FLOAT_VEC20x8B50, mImageProgramTransform, xformData,
2630 mImageProgramUniformState.mTransform);
2631
2632 MaybeUniformData(LOCAL_GL_FLOAT_VEC40x8B52, mImageProgramColor, colorData,
2633 mImageProgramUniformState.mColor);
2634
2635 MaybeUniformData(LOCAL_GL_FLOAT0x1406, mImageProgramSwizzle, swizzleData,
2636 mImageProgramUniformState.mSwizzle);
2637
2638 // Start binding the WebGL state for the texture.
2639 BackingTexture* backing = nullptr;
2640 if (handle) {
2641 backing = handle->GetBackingTexture();
2642 if (!tex) {
2643 tex = backing->GetWebGLTexture();
2644 }
2645 bounds = handle->GetBounds();
2646 backingSize = backing->GetSize();
2647 }
2648 if (mLastTexture != tex) {
2649 mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, tex);
2650 mLastTexture = tex;
2651 }
2652
2653 if (backing && !backing->IsInitialized()) {
2654 // If this is the first time the texture is used, we need to initialize
2655 // the clamping and filtering state.
2656 backing->MarkInitialized();
2657 InitTexParameters(tex);
2658 if (texSize != backingSize) {
2659 // If this is a shared texture handle whose actual backing texture is
2660 // larger than it, then we need to allocate the texture page to the
2661 // full backing size before we can do a partial upload of the surface.
2662 UploadSurface(nullptr, format, IntRect(IntPoint(), backingSize),
2663 IntPoint(), true, true);
2664 }
2665 }
2666
2667 if (data) {
2668 UploadSurface(data, format, IntRect(offset, texSize), bounds.TopLeft(),
2669 texSize == backingSize);
2670 // Signal that we had to upload new data to the texture cache.
2671 mCurrentTarget->mProfile.OnCacheMiss();
2672 } else {
2673 // Signal that we are reusing data from the texture cache.
2674 mCurrentTarget->mProfile.OnCacheHit();
2675 }
2676
2677 // Set up the texture coordinate matrix to map from the input rectangle to
2678 // the backing texture subrect.
2679 Size backingSizeF(backingSize);
2680 Matrix uvMatrix(aRect.width, 0.0f, 0.0f, aRect.height, aRect.x, aRect.y);
2681 uvMatrix *= invMatrix;
2682 uvMatrix *= Matrix(1.0f / backingSizeF.width, 0.0f, 0.0f,
2683 1.0f / backingSizeF.height,
2684 float(bounds.x - offset.x) / backingSizeF.width,
2685 float(bounds.y - offset.y) / backingSizeF.height);
2686 Array<float, 6> uvData = {uvMatrix._11, uvMatrix._12, uvMatrix._21,
2687 uvMatrix._22, uvMatrix._31, uvMatrix._32};
2688 MaybeUniformData(LOCAL_GL_FLOAT_VEC20x8B50, mImageProgramTexMatrix, uvData,
2689 mImageProgramUniformState.mTexMatrix);
2690
2691 // Clamp sampling to within the bounds of the backing texture subrect.
2692 Array<float, 4> texBounds = {
2693 (bounds.x + 0.5f) / backingSizeF.width,
2694 (bounds.y + 0.5f) / backingSizeF.height,
2695 (bounds.XMost() - 0.5f) / backingSizeF.width,
2696 (bounds.YMost() - 0.5f) / backingSizeF.height,
2697 };
2698 switch (surfacePattern.mExtendMode) {
2699 case ExtendMode::REPEAT:
2700 texBounds[0] = -1e16f;
2701 texBounds[1] = -1e16f;
2702 texBounds[2] = 1e16f;
2703 texBounds[3] = 1e16f;
2704 break;
2705 case ExtendMode::REPEAT_X:
2706 texBounds[0] = -1e16f;
2707 texBounds[2] = 1e16f;
2708 break;
2709 case ExtendMode::REPEAT_Y:
2710 texBounds[1] = -1e16f;
2711 texBounds[3] = 1e16f;
2712 break;
2713 default:
2714 break;
2715 }
2716 MaybeUniformData(LOCAL_GL_FLOAT_VEC40x8B52, mImageProgramTexBounds, texBounds,
2717 mImageProgramUniformState.mTexBounds);
2718
2719 // Ensure we use nearest filtering when no antialiasing is requested.
2720 if (UseNearestFilter(surfacePattern)) {
2721 SetTexFilter(tex, false);
2722 }
2723
2724 // Finally draw the image rectangle.
2725 if (aVertexRange) {
2726 // If there's a vertex range, then we need to draw triangles within from
2727 // generated from a path stored in the path vertex buffer.
2728 DrawTriangles(*aVertexRange);
2729 } else {
2730 // Otherwise we're drawing a simple filled rectangle.
2731 DrawQuad();
2732 }
2733
2734 // Restore the default linear filter if overridden.
2735 if (UseNearestFilter(surfacePattern)) {
2736 SetTexFilter(tex, true);
2737 }
2738
2739 success = true;
2740 break;
2741 }
2742 default:
2743 gfxWarningmozilla::gfx::WarningLog() << "Unknown DrawTargetWebgl::DrawRect pattern type: "
2744 << (int)aPattern.GetType();
2745 break;
2746 }
2747
2748 return success;
2749}
2750
2751bool SharedContextWebgl::RemoveSharedTexture(
2752 const RefPtr<SharedTexture>& aTexture) {
2753 auto pos =
2754 std::find(mSharedTextures.begin(), mSharedTextures.end(), aTexture);
2755 if (pos == mSharedTextures.end()) {
2756 return false;
2757 }
2758 // Keep around a reserve of empty pages to avoid initialization costs from
2759 // allocating shared pages. If still below the limit of reserved pages, then
2760 // just add it to the reserve. Otherwise, erase the empty texture page.
2761 size_t maxBytes = StaticPrefs::gfx_canvas_accelerated_reserve_empty_cache()
2762 << 20;
2763 size_t totalEmpty = mEmptyTextureMemory + aTexture->UsedBytes();
2764 if (totalEmpty <= maxBytes) {
2765 mEmptyTextureMemory = totalEmpty;
2766 } else {
2767 RemoveTextureMemory(aTexture);
2768 mSharedTextures.erase(pos);
2769 ClearLastTexture();
2770 }
2771 return true;
2772}
2773
2774void SharedTextureHandle::Cleanup(SharedContextWebgl& aContext) {
2775 mTexture->Free(*this);
2776
2777 // Check if the shared handle's owning page has no more allocated handles
2778 // after we freed it. If so, remove the empty shared texture page also.
2779 if (!mTexture->HasAllocatedHandles()) {
2780 aContext.RemoveSharedTexture(mTexture);
2781 }
2782}
2783
2784bool SharedContextWebgl::RemoveStandaloneTexture(
2785 const RefPtr<StandaloneTexture>& aTexture) {
2786 auto pos = std::find(mStandaloneTextures.begin(), mStandaloneTextures.end(),
2787 aTexture);
2788 if (pos == mStandaloneTextures.end()) {
2789 return false;
2790 }
2791 RemoveTextureMemory(aTexture);
2792 mStandaloneTextures.erase(pos);
2793 ClearLastTexture();
2794 return true;
2795}
2796
2797void StandaloneTexture::Cleanup(SharedContextWebgl& aContext) {
2798 aContext.RemoveStandaloneTexture(this);
2799}
2800
2801// Prune a given texture handle and release its associated resources.
2802void SharedContextWebgl::PruneTextureHandle(
2803 const RefPtr<TextureHandle>& aHandle) {
2804 // Invalidate the handle so nothing will subsequently use its contents.
2805 aHandle->Invalidate();
2806 // If the handle has an associated SourceSurface, unlink it.
2807 UnlinkSurfaceTexture(aHandle);
2808 // If the handle has an associated CacheEntry, unlink it.
2809 if (RefPtr<CacheEntry> entry = aHandle->GetCacheEntry()) {
2810 entry->Unlink();
2811 }
2812 // Deduct the used space from the total.
2813 mUsedTextureMemory -= aHandle->UsedBytes();
2814 // Ensure any allocated shared or standalone texture regions get freed.
2815 aHandle->Cleanup(*this);
2816}
2817
2818// Prune any texture memory above the limit (or margin below the limit) or any
2819// least-recently-used handles that are no longer associated with any usable
2820// surface.
2821bool SharedContextWebgl::PruneTextureMemory(size_t aMargin, bool aPruneUnused) {
2822 // The maximum amount of texture memory that may be used by textures.
2823 size_t maxBytes = StaticPrefs::gfx_canvas_accelerated_cache_size() << 20;
2824 maxBytes -= std::min(maxBytes, aMargin);
2825 size_t maxItems = StaticPrefs::gfx_canvas_accelerated_cache_items();
2826 size_t oldItems = mNumTextureHandles;
2827 while (!mTextureHandles.isEmpty() &&
2828 (mUsedTextureMemory > maxBytes || mNumTextureHandles > maxItems ||
2829 (aPruneUnused && !mTextureHandles.getLast()->IsUsed()))) {
2830 PruneTextureHandle(mTextureHandles.popLast());
2831 --mNumTextureHandles;
2832 }
2833 return mNumTextureHandles < oldItems;
2834}
2835
2836// Attempt to convert a linear gradient to a 1D ramp texture.
2837Maybe<SurfacePattern> DrawTargetWebgl::LinearGradientToSurface(
2838 const RectDouble& aBounds, const Pattern& aPattern) {
2839 MOZ_ASSERT(aPattern.GetType() == PatternType::LINEAR_GRADIENT)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aPattern.GetType() == PatternType::LINEAR_GRADIENT)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aPattern.GetType() == PatternType::LINEAR_GRADIENT))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aPattern.GetType() == PatternType::LINEAR_GRADIENT"
, "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 2839)
; AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPattern.GetType() == PatternType::LINEAR_GRADIENT"
")"); do { MOZ_CrashSequence(__null, 2839); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2840 const auto& gradient = static_cast<const LinearGradientPattern&>(aPattern);
2841 // The gradient points must be transformed by the gradient's matrix.
2842 Point gradBegin = gradient.mMatrix.TransformPoint(gradient.mBegin);
2843 Point gradEnd = gradient.mMatrix.TransformPoint(gradient.mEnd);
2844 // Get the gradient points in user-space.
2845 Point begin = mTransform.TransformPoint(gradBegin);
2846 Point end = mTransform.TransformPoint(gradEnd);
2847 // Find the normalized direction of the gradient and its length.
2848 Point dir = end - begin;
2849 float len = dir.Length();
2850 dir = dir / len;
2851 // Restrict the rendered bounds to fall within the canvas.
2852 Rect visBounds = NarrowToFloat(aBounds.SafeIntersect(RectDouble(GetRect())));
2853 // Calculate the distances along the gradient direction of the bounds.
2854 float dist0 = (visBounds.TopLeft() - begin).DotProduct(dir);
2855 float distX = visBounds.width * dir.x;
2856 float distY = visBounds.height * dir.y;
2857 float minDist = floorf(
2858 std::max(dist0 + std::min(distX, 0.0f) + std::min(distY, 0.0f), 0.0f));
2859 float maxDist = ceilf(
2860 std::min(dist0 + std::max(distX, 0.0f) + std::max(distY, 0.0f), len));
2861 // Calculate the approximate size of the ramp texture, and see if it would be
2862 // sufficiently smaller than just rendering the primitive.
2863 float subLen = maxDist - minDist;
2864 if (subLen > 0 && subLen < 0.5f * visBounds.Area()) {
2865 // Create a 1D texture to contain the gradient ramp. Reserve two extra
2866 // texels at the beginning and end of the ramp to account for clamping.
2867 RefPtr<DrawTargetSkia> dt = new DrawTargetSkia;
2868 if (dt->Init(IntSize(int32_t(subLen + 2), 1), SurfaceFormat::B8G8R8A8)) {
2869 // Fill the section of the gradient ramp that is actually used.
2870 dt->FillRect(Rect(dt->GetRect()),
2871 LinearGradientPattern(Point(1 - minDist, 0.0f),
2872 Point(len + 1 - minDist, 0.0f),
2873 gradient.mStops));
2874 if (RefPtr<SourceSurface> snapshot = dt->Snapshot()) {
2875 // Calculate a matrix that will map the gradient ramp texture onto the
2876 // actual direction of the gradient.
2877 Point gradDir = (gradEnd - gradBegin) / len;
2878 Point tangent = Point(-gradDir.y, gradDir.x) / gradDir.Length();
2879 SurfacePattern surfacePattern(
2880 snapshot, ExtendMode::CLAMP,
2881 Matrix(gradDir.x, gradDir.y, tangent.x, tangent.y, gradBegin.x,
2882 gradBegin.y)
2883 .PreTranslate(minDist - 1, 0));
2884 if (SupportsPattern(surfacePattern)) {
2885 return Some(surfacePattern);
2886 }
2887 }
2888 }
2889 }
2890 return Nothing();
2891}
2892
2893void DrawTargetWebgl::FillRect(const Rect& aRect, const Pattern& aPattern,
2894 const DrawOptions& aOptions) {
2895 RectDouble xformRect = TransformDouble(aRect);
2896 if (aPattern.GetType() == PatternType::COLOR) {
2897 if (Maybe<Rect> clipped = RectClippedToViewport(xformRect)) {
2898 // If the pattern is transform-invariant and the rect clips to the
2899 // viewport, just clip drawing to the viewport to avoid transform
2900 // issues.
2901 DrawRect(*clipped, aPattern, aOptions, Nothing(), nullptr, false);
2902 return;
2903 }
2904 }
2905 if (RectInsidePrecisionLimits(xformRect)) {
2906 if (SupportsPattern(aPattern)) {
2907 DrawRect(aRect, aPattern, aOptions);
2908 return;
2909 }
2910 if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
2911 if (Maybe<SurfacePattern> surface =
2912 LinearGradientToSurface(xformRect, aPattern)) {
2913 if (DrawRect(aRect, *surface, aOptions, Nothing(), nullptr, true, true,
2914 true)) {
2915 return;
2916 }
2917 }
2918 }
2919 }
2920
2921 if (!mWebglValid) {
2922 MarkSkiaChanged(aOptions);
2923 mSkia->FillRect(aRect, aPattern, aOptions);
2924 } else {
2925 // If the pattern is unsupported, then transform the rect to a path so it
2926 // can be cached.
2927 SkPath skiaPath;
2928 skiaPath.addRect(RectToSkRect(aRect));
2929 RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
2930 DrawPath(path, aPattern, aOptions);
2931 }
2932}
2933
2934void CacheEntry::Link(const RefPtr<TextureHandle>& aHandle) {
2935 mHandle = aHandle;
2936 mHandle->SetCacheEntry(this);
2937}
2938
2939// When the CacheEntry becomes unused, it marks the corresponding
2940// TextureHandle as unused and unlinks it from the CacheEntry. The
2941// entry is removed from its containing Cache, if applicable.
2942void CacheEntry::Unlink() {
2943 // The entry may not have a valid handle if rasterization failed.
2944 if (mHandle) {
8
Taking false branch
2945 mHandle->SetCacheEntry(nullptr);
2946 mHandle = nullptr;
2947 }
2948
2949 RemoveFromList();
9
Calling 'CacheEntryImpl::RemoveFromList'
27
Returning; memory was released
2950}
2951
2952// Hashes a path and pattern to a single hash value that can be used for quick
2953// comparisons. This currently avoids to expensive hashing of internal path
2954// and pattern data for speed, relying instead on later exact comparisons for
2955// disambiguation.
2956HashNumber PathCacheEntry::HashPath(const QuantizedPath& aPath,
2957 const Pattern* aPattern,
2958 const Matrix& aTransform,
2959 const IntRect& aBounds,
2960 const Point& aOrigin) {
2961 HashNumber hash = 0;
2962 hash = AddToHash(hash, aPath.mPath.num_types);
2963 hash = AddToHash(hash, aPath.mPath.num_points);
2964 if (aPath.mPath.num_points > 0) {
2965 hash = AddToHash(hash, aPath.mPath.points[0].x);
2966 hash = AddToHash(hash, aPath.mPath.points[0].y);
2967 if (aPath.mPath.num_points > 1) {
2968 hash = AddToHash(hash, aPath.mPath.points[1].x);
2969 hash = AddToHash(hash, aPath.mPath.points[1].y);
2970 }
2971 }
2972 // Quantize the relative offset of the path to its bounds.
2973 IntPoint offset = RoundedToInt((aOrigin - Point(aBounds.TopLeft())) * 16.0f);
2974 hash = AddToHash(hash, offset.x);
2975 hash = AddToHash(hash, offset.y);
2976 hash = AddToHash(hash, aBounds.width);
2977 hash = AddToHash(hash, aBounds.height);
2978 if (aPattern) {
2979 hash = AddToHash(hash, (int)aPattern->GetType());
2980 }
2981 return hash;
2982}
2983
2984// When caching rendered geometry, we need to ensure the scale and orientation
2985// is approximately the same. The offset will be considered separately.
2986static inline bool HasMatchingScale(const Matrix& aTransform1,
2987 const Matrix& aTransform2) {
2988 return FuzzyEqual(aTransform1._11, aTransform2._11) &&
2989 FuzzyEqual(aTransform1._22, aTransform2._22) &&
2990 FuzzyEqual(aTransform1._12, aTransform2._12) &&
2991 FuzzyEqual(aTransform1._21, aTransform2._21);
2992}
2993
2994// Determines if an existing path cache entry matches an incoming path and
2995// pattern.
2996inline bool PathCacheEntry::MatchesPath(
2997 const QuantizedPath& aPath, const Pattern* aPattern,
2998 const StrokeOptions* aStrokeOptions, AAStrokeMode aStrokeMode,
2999 const Matrix& aTransform, const IntRect& aBounds, const Point& aOrigin,
3000 HashNumber aHash, float aSigma) {
3001 return aHash == mHash && HasMatchingScale(aTransform, mTransform) &&
3002 // Ensure the clipped relative bounds fit inside those of the entry
3003 aBounds.x - aOrigin.x >= mBounds.x - mOrigin.x &&
3004 (aBounds.x - aOrigin.x) + aBounds.width <=
3005 (mBounds.x - mOrigin.x) + mBounds.width &&
3006 aBounds.y - aOrigin.y >= mBounds.y - mOrigin.y &&
3007 (aBounds.y - aOrigin.y) + aBounds.height <=
3008 (mBounds.y - mOrigin.y) + mBounds.height &&
3009 aPath == mPath &&
3010 (!aPattern ? !mPattern : mPattern && *aPattern == *mPattern) &&
3011 (!aStrokeOptions
3012 ? !mStrokeOptions
3013 : mStrokeOptions && *aStrokeOptions == *mStrokeOptions &&
3014 mAAStrokeMode == aStrokeMode) &&
3015 aSigma == mSigma;
3016}
3017
3018PathCacheEntry::PathCacheEntry(QuantizedPath&& aPath, Pattern* aPattern,
3019 StoredStrokeOptions* aStrokeOptions,
3020 AAStrokeMode aStrokeMode,
3021 const Matrix& aTransform, const IntRect& aBounds,
3022 const Point& aOrigin, HashNumber aHash,
3023 float aSigma)
3024 : CacheEntryImpl<PathCacheEntry>(aTransform, aBounds, aHash),
3025 mPath(std::move(aPath)),
3026 mOrigin(aOrigin),
3027 mPattern(aPattern),
3028 mStrokeOptions(aStrokeOptions),
3029 mAAStrokeMode(aStrokeMode),
3030 mSigma(aSigma) {}
3031
3032// Attempt to find a matching entry in the path cache. If one isn't found,
3033// a new entry will be created. The caller should check whether the contained
3034// texture handle is valid to determine if it will need to render the text run
3035// or just reuse the cached texture.
3036already_AddRefed<PathCacheEntry> PathCache::FindOrInsertEntry(
3037 QuantizedPath aPath, const Pattern* aPattern,
3038 const StrokeOptions* aStrokeOptions, AAStrokeMode aStrokeMode,
3039 const Matrix& aTransform, const IntRect& aBounds, const Point& aOrigin,
3040 float aSigma) {
3041 HashNumber hash =
3042 PathCacheEntry::HashPath(aPath, aPattern, aTransform, aBounds, aOrigin);
3043 for (const RefPtr<PathCacheEntry>& entry : GetChain(hash)) {
3044 if (entry->MatchesPath(aPath, aPattern, aStrokeOptions, aStrokeMode,
3045 aTransform, aBounds, aOrigin, hash, aSigma)) {
3046 return do_AddRef(entry);
3047 }
3048 }
3049 Pattern* pattern = nullptr;
3050 if (aPattern) {
3051 pattern = aPattern->CloneWeak();
3052 if (!pattern) {
3053 return nullptr;
3054 }
3055 }
3056 StoredStrokeOptions* strokeOptions = nullptr;
3057 if (aStrokeOptions) {
3058 strokeOptions = aStrokeOptions->Clone();
3059 if (!strokeOptions) {
3060 return nullptr;
3061 }
3062 }
3063 RefPtr<PathCacheEntry> entry =
3064 new PathCacheEntry(std::move(aPath), pattern, strokeOptions, aStrokeMode,
3065 aTransform, aBounds, aOrigin, hash, aSigma);
3066 Insert(entry);
3067 return entry.forget();
3068}
3069
3070void DrawTargetWebgl::Fill(const Path* aPath, const Pattern& aPattern,
3071 const DrawOptions& aOptions) {
3072 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
3073 return;
3074 }
3075
3076 const SkPath& skiaPath = static_cast<const PathSkia*>(aPath)->GetPath();
3077 SkRect skiaRect = SkRect::MakeEmpty();
3078 // Draw the path as a simple rectangle with a supported pattern when possible.
3079 if (skiaPath.isRect(&skiaRect)) {
3080 RectDouble rect = SkRectToRectDouble(skiaRect);
3081 RectDouble xformRect = TransformDouble(rect);
3082 if (aPattern.GetType() == PatternType::COLOR) {
3083 if (Maybe<Rect> clipped = RectClippedToViewport(xformRect)) {
3084 // If the pattern is transform-invariant and the rect clips to the
3085 // viewport, just clip drawing to the viewport to avoid transform
3086 // issues.
3087 DrawRect(*clipped, aPattern, aOptions, Nothing(), nullptr, false);
3088 return;
3089 }
3090 }
3091
3092 if (RectInsidePrecisionLimits(xformRect)) {
3093 if (SupportsPattern(aPattern)) {
3094 DrawRect(NarrowToFloat(rect), aPattern, aOptions);
3095 return;
3096 }
3097 if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
3098 if (Maybe<SurfacePattern> surface =
3099 LinearGradientToSurface(xformRect, aPattern)) {
3100 if (DrawRect(NarrowToFloat(rect), *surface, aOptions, Nothing(),
3101 nullptr, true, true, true)) {
3102 return;
3103 }
3104 }
3105 }
3106 }
3107 }
3108
3109 DrawPath(aPath, aPattern, aOptions);
3110}
3111
3112void DrawTargetWebgl::FillCircle(const Point& aOrigin, float aRadius,
3113 const Pattern& aPattern,
3114 const DrawOptions& aOptions) {
3115 DrawCircle(aOrigin, aRadius, aPattern, aOptions);
3116}
3117
3118QuantizedPath::QuantizedPath(const WGR::Path& aPath) : mPath(aPath) {}
3119
3120QuantizedPath::QuantizedPath(QuantizedPath&& aPath) noexcept
3121 : mPath(aPath.mPath) {
3122 aPath.mPath.points = nullptr;
3123 aPath.mPath.num_points = 0;
3124 aPath.mPath.types = nullptr;
3125 aPath.mPath.num_types = 0;
3126}
3127
3128QuantizedPath::~QuantizedPath() {
3129 if (mPath.points || mPath.types) {
3130 WGR::wgr_path_release(mPath);
3131 }
3132}
3133
3134bool QuantizedPath::operator==(const QuantizedPath& aOther) const {
3135 return mPath.num_types == aOther.mPath.num_types &&
3136 mPath.num_points == aOther.mPath.num_points &&
3137 mPath.fill_mode == aOther.mPath.fill_mode &&
3138 !memcmp(mPath.types, aOther.mPath.types,
3139 mPath.num_types * sizeof(uint8_t)) &&
3140 !memcmp(mPath.points, aOther.mPath.points,
3141 mPath.num_points * sizeof(WGR::Point));
3142}
3143
3144// Generate a quantized path from the Skia path using WGR. The supplied
3145// transform will be applied to the path. The path is stored relative to its
3146// bounds origin to support translation later.
3147static Maybe<QuantizedPath> GenerateQuantizedPath(
3148 WGR::PathBuilder* aPathBuilder, const SkPath& aPath, const Rect& aBounds,
3149 const Matrix& aTransform) {
3150 if (!aPathBuilder) {
3151 return Nothing();
3152 }
3153
3154 WGR::wgr_builder_reset(aPathBuilder);
3155 WGR::wgr_builder_set_fill_mode(aPathBuilder,
3156 aPath.getFillType() == SkPathFillType::kWinding
3157 ? WGR::FillMode::Winding
3158 : WGR::FillMode::EvenOdd);
3159
3160 SkPath::RawIter iter(aPath);
3161 SkPoint params[4];
3162 SkPath::Verb currentVerb;
3163
3164 // printf_stderr("bounds: (%d, %d) %d x %d\n", aBounds.x, aBounds.y,
3165 // aBounds.width, aBounds.height);
3166 Matrix transform = aTransform;
3167 transform.PostTranslate(-aBounds.TopLeft());
3168 while ((currentVerb = iter.next(params)) != SkPath::kDone_Verb) {
3169 switch (currentVerb) {
3170 case SkPath::kMove_Verb: {
3171 Point p0 = transform.TransformPoint(SkPointToPoint(params[0]));
3172 WGR::wgr_builder_move_to(aPathBuilder, p0.x, p0.y);
3173 break;
3174 }
3175 case SkPath::kLine_Verb: {
3176 Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
3177 WGR::wgr_builder_line_to(aPathBuilder, p1.x, p1.y);
3178 break;
3179 }
3180 case SkPath::kCubic_Verb: {
3181 Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
3182 Point p2 = transform.TransformPoint(SkPointToPoint(params[2]));
3183 Point p3 = transform.TransformPoint(SkPointToPoint(params[3]));
3184 // printf_stderr("cubic (%f, %f), (%f, %f), (%f, %f)\n", p1.x, p1.y,
3185 // p2.x, p2.y, p3.x, p3.y);
3186 WGR::wgr_builder_curve_to(aPathBuilder, p1.x, p1.y, p2.x, p2.y, p3.x,
3187 p3.y);
3188 break;
3189 }
3190 case SkPath::kQuad_Verb: {
3191 Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
3192 Point p2 = transform.TransformPoint(SkPointToPoint(params[2]));
3193 // printf_stderr("quad (%f, %f), (%f, %f)\n", p1.x, p1.y, p2.x, p2.y);
3194 WGR::wgr_builder_quad_to(aPathBuilder, p1.x, p1.y, p2.x, p2.y);
3195 break;
3196 }
3197 case SkPath::kConic_Verb: {
3198 Point p0 = transform.TransformPoint(SkPointToPoint(params[0]));
3199 Point p1 = transform.TransformPoint(SkPointToPoint(params[1]));
3200 Point p2 = transform.TransformPoint(SkPointToPoint(params[2]));
3201 float w = iter.conicWeight();
3202 std::vector<Point> quads;
3203 int numQuads = ConvertConicToQuads(p0, p1, p2, w, quads);
3204 for (int i = 0; i < numQuads; i++) {
3205 Point q1 = quads[2 * i + 1];
3206 Point q2 = quads[2 * i + 2];
3207 // printf_stderr("conic quad (%f, %f), (%f, %f)\n", q1.x, q1.y, q2.x,
3208 // q2.y);
3209 WGR::wgr_builder_quad_to(aPathBuilder, q1.x, q1.y, q2.x, q2.y);
3210 }
3211 break;
3212 }
3213 case SkPath::kClose_Verb:
3214 // printf_stderr("close\n");
3215 WGR::wgr_builder_close(aPathBuilder);
3216 break;
3217 default:
3218 MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 3218); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")")
; do { MOZ_CrashSequence(__null, 3218); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3219 // Unexpected verb found in path!
3220 return Nothing();
3221 }
3222 }
3223
3224 WGR::Path p = WGR::wgr_builder_get_path(aPathBuilder);
3225 if (!p.num_points || !p.num_types) {
3226 WGR::wgr_path_release(p);
3227 return Nothing();
3228 }
3229 return Some(QuantizedPath(p));
3230}
3231
3232// Get the output vertex buffer using WGR from an input quantized path.
3233static Maybe<WGR::VertexBuffer> GeneratePathVertexBuffer(
3234 const QuantizedPath& aPath, const IntRect& aClipRect,
3235 bool aRasterizationTruncates, WGR::OutputVertex* aBuffer,
3236 size_t aBufferCapacity) {
3237 WGR::VertexBuffer vb = WGR::wgr_path_rasterize_to_tri_list(
3238 &aPath.mPath, aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height,
3239 true, false, aRasterizationTruncates, aBuffer, aBufferCapacity);
3240 if (!vb.len || (aBuffer && vb.len > aBufferCapacity)) {
3241 WGR::wgr_vertex_buffer_release(vb);
3242 return Nothing();
3243 }
3244 return Some(vb);
3245}
3246
3247static inline AAStroke::LineJoin ToAAStrokeLineJoin(JoinStyle aJoin) {
3248 switch (aJoin) {
3249 case JoinStyle::BEVEL:
3250 return AAStroke::LineJoin::Bevel;
3251 case JoinStyle::ROUND:
3252 return AAStroke::LineJoin::Round;
3253 case JoinStyle::MITER:
3254 case JoinStyle::MITER_OR_BEVEL:
3255 return AAStroke::LineJoin::Miter;
3256 }
3257 return AAStroke::LineJoin::Miter;
3258}
3259
3260static inline AAStroke::LineCap ToAAStrokeLineCap(CapStyle aCap) {
3261 switch (aCap) {
3262 case CapStyle::BUTT:
3263 return AAStroke::LineCap::Butt;
3264 case CapStyle::ROUND:
3265 return AAStroke::LineCap::Round;
3266 case CapStyle::SQUARE:
3267 return AAStroke::LineCap::Square;
3268 }
3269 return AAStroke::LineCap::Butt;
3270}
3271
3272static inline Point WGRPointToPoint(const WGR::Point& aPoint) {
3273 // WGR points are 28.4 fixed-point where (0.0, 0.0) is assumed to be a pixel
3274 // center, as opposed to (0.5, 0.5) in canvas device space. WGR thus shifts
3275 // each point by (-0.5, -0.5). To undo this, transform from fixed-point back
3276 // to floating-point, and reverse the pixel shift by adding back (0.5, 0.5).
3277 return Point(IntPoint(aPoint.x, aPoint.y)) * (1.0f / 16.0f) +
3278 Point(0.5f, 0.5f);
3279}
3280
3281// Generates a vertex buffer for a stroked path using aa-stroke.
3282static Maybe<AAStroke::VertexBuffer> GenerateStrokeVertexBuffer(
3283 const QuantizedPath& aPath, const StrokeOptions* aStrokeOptions,
3284 float aScale, WGR::OutputVertex* aBuffer, size_t aBufferCapacity) {
3285 AAStroke::StrokeStyle style = {aStrokeOptions->mLineWidth * aScale,
3286 ToAAStrokeLineCap(aStrokeOptions->mLineCap),
3287 ToAAStrokeLineJoin(aStrokeOptions->mLineJoin),
3288 aStrokeOptions->mMiterLimit};
3289 if (style.width <= 0.0f || !std::isfinite(style.width) ||
3290 style.miter_limit <= 0.0f || !std::isfinite(style.miter_limit)) {
3291 return Nothing();
3292 }
3293 AAStroke::Stroker* s = AAStroke::aa_stroke_new(
3294 &style, (AAStroke::OutputVertex*)aBuffer, aBufferCapacity);
3295 bool valid = true;
3296 size_t curPoint = 0;
3297 for (size_t curType = 0; valid && curType < aPath.mPath.num_types;) {
3298 // Verify that we are at the start of a sub-path.
3299 if ((aPath.mPath.types[curType] & WGR::PathPointTypePathTypeMask) !=
3300 WGR::PathPointTypeStart) {
3301 valid = false;
3302 break;
3303 }
3304 // Find where the next sub-path starts so we can locate the end.
3305 size_t endType = curType + 1;
3306 for (; endType < aPath.mPath.num_types; endType++) {
3307 if ((aPath.mPath.types[endType] & WGR::PathPointTypePathTypeMask) ==
3308 WGR::PathPointTypeStart) {
3309 break;
3310 }
3311 }
3312 // Check if the path is closed. This is a flag modifying the last type.
3313 bool closed =
3314 (aPath.mPath.types[endType - 1] & WGR::PathPointTypeCloseSubpath) != 0;
3315 for (; curType < endType; curType++) {
3316 // If this is the last type and the sub-path is not closed, determine if
3317 // this segment should be capped.
3318 bool end = curType + 1 == endType && !closed;
3319 switch (aPath.mPath.types[curType] & WGR::PathPointTypePathTypeMask) {
3320 case WGR::PathPointTypeStart: {
3321 if (curPoint + 1 > aPath.mPath.num_points) {
3322 valid = false;
3323 break;
3324 }
3325 Point p1 = WGRPointToPoint(aPath.mPath.points[curPoint]);
3326 AAStroke::aa_stroke_move_to(s, p1.x, p1.y, closed);
3327 if (end) {
3328 AAStroke::aa_stroke_line_to(s, p1.x, p1.y, true);
3329 }
3330 curPoint++;
3331 break;
3332 }
3333 case WGR::PathPointTypeLine: {
3334 if (curPoint + 1 > aPath.mPath.num_points) {
3335 valid = false;
3336 break;
3337 }
3338 Point p1 = WGRPointToPoint(aPath.mPath.points[curPoint]);
3339 AAStroke::aa_stroke_line_to(s, p1.x, p1.y, end);
3340 curPoint++;
3341 break;
3342 }
3343 case WGR::PathPointTypeBezier: {
3344 if (curPoint + 3 > aPath.mPath.num_points) {
3345 valid = false;
3346 break;
3347 }
3348 Point p1 = WGRPointToPoint(aPath.mPath.points[curPoint]);
3349 Point p2 = WGRPointToPoint(aPath.mPath.points[curPoint + 1]);
3350 Point p3 = WGRPointToPoint(aPath.mPath.points[curPoint + 2]);
3351 AAStroke::aa_stroke_curve_to(s, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y,
3352 end);
3353 curPoint += 3;
3354 break;
3355 }
3356 default:
3357 MOZ_ASSERT(false, "Unknown WGR path point type")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "Unknown WGR path point type"
")", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 3357
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") (" "Unknown WGR path point type"
")"); do { MOZ_CrashSequence(__null, 3357); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3358 valid = false;
3359 break;
3360 }
3361 }
3362 // Close the sub-path if necessary.
3363 if (valid && closed) {
3364 AAStroke::aa_stroke_close(s);
3365 }
3366 }
3367 Maybe<AAStroke::VertexBuffer> result;
3368 if (valid) {
3369 AAStroke::VertexBuffer vb = AAStroke::aa_stroke_finish(s);
3370 if (!vb.len || (aBuffer && vb.len > aBufferCapacity)) {
3371 AAStroke::aa_stroke_vertex_buffer_release(vb);
3372 } else {
3373 result = Some(vb);
3374 }
3375 }
3376 AAStroke::aa_stroke_release(s);
3377 return result;
3378}
3379
3380// Search the path cache for any entries stored in the path vertex buffer and
3381// remove them.
3382void PathCache::ClearVertexRanges() {
3383 for (auto& chain : mChains) {
3384 PathCacheEntry* entry = chain.getFirst();
3385 while (entry) {
3386 PathCacheEntry* next = entry->getNext();
3387 if (entry->GetVertexRange().IsValid()) {
3388 entry->Unlink();
3389 }
3390 entry = next;
3391 }
3392 }
3393}
3394
3395inline bool DrawTargetWebgl::ShouldAccelPath(
3396 const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions) {
3397 return mWebglValid && SupportsDrawOptions(aOptions) && PrepareContext();
3398}
3399
3400// For now, we only directly support stroking solid color patterns to limit
3401// artifacts from blending of overlapping geometry generated by AAStroke. Other
3402// types of patterns may be partially supported by rendering to a temporary
3403// mask.
3404static inline AAStrokeMode SupportsAAStroke(const Pattern& aPattern,
3405 const DrawOptions& aOptions,
3406 const StrokeOptions& aStrokeOptions,
3407 bool aAllowStrokeAlpha) {
3408 if (aStrokeOptions.mDashPattern) {
3409 return AAStrokeMode::Unsupported;
3410 }
3411 switch (aOptions.mCompositionOp) {
3412 case CompositionOp::OP_SOURCE:
3413 return AAStrokeMode::Geometry;
3414 case CompositionOp::OP_OVER:
3415 if (aPattern.GetType() == PatternType::COLOR) {
3416 return static_cast<const ColorPattern&>(aPattern).mColor.a *
3417 aOptions.mAlpha <
3418 1.0f &&
3419 !aAllowStrokeAlpha
3420 ? AAStrokeMode::Mask
3421 : AAStrokeMode::Geometry;
3422 }
3423 return AAStrokeMode::Unsupported;
3424 default:
3425 return AAStrokeMode::Unsupported;
3426 }
3427}
3428
3429// Render an AA-Stroke'd vertex range into an R8 mask texture for subsequent
3430// drawing.
3431already_AddRefed<TextureHandle> SharedContextWebgl::DrawStrokeMask(
3432 const PathVertexRange& aVertexRange, const IntSize& aSize) {
3433 // Allocate a new texture handle to store the rendered mask.
3434 RefPtr<TextureHandle> handle =
3435 AllocateTextureHandle(SurfaceFormat::A8, aSize, true, true);
3436 if (!handle) {
3437 return nullptr;
3438 }
3439
3440 IntRect texBounds = handle->GetBounds();
3441 BackingTexture* backing = handle->GetBackingTexture();
3442 if (!backing->IsInitialized()) {
3443 // If the backing texture is uninitialized, it needs its sampling parameters
3444 // set for later use.
3445 mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D0x0DE1, backing->GetWebGLTexture());
3446 mWebgl->TexStorage(LOCAL_GL_TEXTURE_2D0x0DE1, 1, LOCAL_GL_R80x8229,
3447 {uint32_t(backing->GetSize().width),
3448 uint32_t(backing->GetSize().height), 1});
3449 InitTexParameters(backing->GetWebGLTexture());
3450 ClearLastTexture();
3451 }
3452
3453 // Set up a scratch framebuffer to render to the appropriate sub-texture of
3454 // the backing texture.
3455 if (!mScratchFramebuffer) {
3456 mScratchFramebuffer = mWebgl->CreateFramebuffer();
3457 }
3458 mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, mScratchFramebuffer);
3459 webgl::FbAttachInfo attachInfo;
3460 attachInfo.tex = backing->GetWebGLTexture();
3461 mWebgl->FramebufferAttach(LOCAL_GL_FRAMEBUFFER0x8D40, LOCAL_GL_COLOR_ATTACHMENT00x8CE0,
3462 LOCAL_GL_TEXTURE_2D0x0DE1, attachInfo);
3463 mWebgl->Viewport(texBounds.x, texBounds.y, texBounds.width, texBounds.height);
3464 EnableScissor(texBounds);
3465 if (!backing->IsInitialized()) {
3466 backing->MarkInitialized();
3467 // WebGL implicitly clears the backing texture the first time it is used.
3468 } else {
3469 // Ensure the mask background is clear.
3470 mWebgl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
3471 mWebgl->Clear(LOCAL_GL_COLOR_BUFFER_BIT0x00004000);
3472 }
3473
3474 // Reset any blending when drawing the mask.
3475 SetBlendState(CompositionOp::OP_OVER);
3476
3477 // Set up the solid color shader to draw a simple opaque mask.
3478 if (mLastProgram != mSolidProgram) {
3479 mWebgl->UseProgram(mSolidProgram);
3480 mLastProgram = mSolidProgram;
3481 }
3482 Array<float, 2> viewportData = {float(texBounds.width),
3483 float(texBounds.height)};
3484 MaybeUniformData(LOCAL_GL_FLOAT_VEC20x8B50, mSolidProgramViewport, viewportData,
3485 mSolidProgramUniformState.mViewport);
3486 Array<float, 1> aaData = {0.0f};
3487 MaybeUniformData(LOCAL_GL_FLOAT0x1406, mSolidProgramAA, aaData,
3488 mSolidProgramUniformState.mAA);
3489 Array<float, 4> clipData = {-0.5f, -0.5f, float(texBounds.width) + 0.5f,
3490 float(texBounds.height) + 0.5f};
3491 MaybeUniformData(LOCAL_GL_FLOAT_VEC40x8B52, mSolidProgramClipBounds, clipData,
3492 mSolidProgramUniformState.mClipBounds);
3493 Array<float, 4> colorData = {1.0f, 1.0f, 1.0f, 1.0f};
3494 MaybeUniformData(LOCAL_GL_FLOAT_VEC40x8B52, mSolidProgramColor, colorData,
3495 mSolidProgramUniformState.mColor);
3496 Array<float, 6> xformData = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
3497 MaybeUniformData(LOCAL_GL_FLOAT_VEC20x8B50, mSolidProgramTransform, xformData,
3498 mSolidProgramUniformState.mTransform);
3499
3500 // Ensure the current clip mask is ignored.
3501 RefPtr<WebGLTexture> prevClipMask = mLastClipMask;
3502 SetNoClipMask();
3503
3504 // Draw the mask using the supplied path vertex range.
3505 DrawTriangles(aVertexRange);
3506
3507 // Restore the previous framebuffer state.
3508 mWebgl->BindFramebuffer(LOCAL_GL_FRAMEBUFFER0x8D40, mCurrentTarget->mFramebuffer);
3509 mWebgl->Viewport(0, 0, mViewportSize.width, mViewportSize.height);
3510 if (prevClipMask) {
3511 SetClipMask(prevClipMask);
3512 }
3513
3514 return handle.forget();
3515}
3516
3517bool SharedContextWebgl::DrawPathAccel(
3518 const Path* aPath, const Pattern& aPattern, const DrawOptions& aOptions,
3519 const StrokeOptions* aStrokeOptions, bool aAllowStrokeAlpha,
3520 const ShadowOptions* aShadow, bool aCacheable, const Matrix* aPathXform) {
3521 // Get the transformed bounds for the path and conservatively check if the
3522 // bounds overlap the canvas.
3523 const PathSkia* pathSkia = static_cast<const PathSkia*>(aPath);
3524 const Matrix& currentTransform = mCurrentTarget->GetTransform();
3525 Matrix pathXform = currentTransform;
3526 // If there is a path-specific transform that shouldn't be applied to the
3527 // pattern, then generate a matrix that should only be used with the Skia
3528 // path.
3529 if (aPathXform) {
3530 pathXform.PreMultiply(*aPathXform);
3531 }
3532 Rect bounds = pathSkia->GetFastBounds(pathXform, aStrokeOptions);
3533 // If the path is empty, then there is nothing to draw.
3534 if (bounds.IsEmpty()) {
3535 return true;
3536 }
3537 // Avoid integer conversion errors with abnormally large paths.
3538 if (!RectInsidePrecisionLimits(bounds)) {
3539 return false;
3540 }
3541 IntRect viewport(IntPoint(), mViewportSize);
3542 if (aShadow) {
3543 // Inflate the bounds to account for the blur radius.
3544 bounds += aShadow->mOffset;
3545 int32_t blurRadius = aShadow->BlurRadius();
3546 bounds.Inflate(blurRadius);
3547 viewport.Inflate(blurRadius);
3548 }
3549 Point realOrigin = bounds.TopLeft();
3550 if (aCacheable) {
3551 // Quantize the path origin to increase the reuse of cache entries.
3552 bounds.Scale(4.0f);
3553 bounds.Round();
3554 bounds.Scale(0.25f);
3555 }
3556 Point quantizedOrigin = bounds.TopLeft();
3557 // If the path doesn't intersect the viewport, then there is nothing to draw.
3558 IntRect intBounds = RoundedOut(bounds).Intersect(viewport);
3559 if (intBounds.IsEmpty()) {
3560 return true;
3561 }
3562 // Nudge the bounds to account for the quantization rounding.
3563 Rect quantBounds = Rect(intBounds) + (realOrigin - quantizedOrigin);
3564 // If the pattern is a solid color, then this will be used along with a path
3565 // mask to render the path, as opposed to baking the pattern into the cached
3566 // path texture.
3567 Maybe<DeviceColor> color =
3568 aOptions.mCompositionOp == CompositionOp::OP_CLEAR
3569 ? Some(DeviceColor(1, 1, 1, 1))
3570 : (aPattern.GetType() == PatternType::COLOR
3571 ? Some(static_cast<const ColorPattern&>(aPattern).mColor)
3572 : Nothing());
3573 AAStrokeMode aaStrokeMode =
3574 aStrokeOptions && mPathAAStroke
3575 ? SupportsAAStroke(aPattern, aOptions, *aStrokeOptions,
3576 aAllowStrokeAlpha)
3577 : AAStrokeMode::Unsupported;
3578 // Look for an existing path cache entry, if possible, or otherwise create
3579 // one. If the draw request is not cacheable, then don't create an entry.
3580 RefPtr<PathCacheEntry> entry;
3581 RefPtr<TextureHandle> handle;
3582 if (aCacheable) {
3583 if (!mPathCache) {
3584 mPathCache = MakeUnique<PathCache>();
3585 }
3586 // Use a quantized, relative (to its bounds origin) version of the path as
3587 // a cache key to help limit cache bloat.
3588 Maybe<QuantizedPath> qp = GenerateQuantizedPath(
3589 mWGRPathBuilder, pathSkia->GetPath(), quantBounds, pathXform);
3590 if (!qp) {
3591 return false;
3592 }
3593 entry = mPathCache->FindOrInsertEntry(
3594 std::move(*qp), color ? nullptr : &aPattern, aStrokeOptions,
3595 aaStrokeMode, currentTransform, intBounds, quantizedOrigin,
3596 aShadow ? aShadow->mSigma : -1.0f);
3597 if (!entry) {
3598 return false;
3599 }
3600 handle = entry->GetHandle();
3601 }
3602
3603 // If there is a shadow, it needs to draw with the shadow color rather than
3604 // the path color.
3605 Maybe<DeviceColor> shadowColor = color;
3606 if (aShadow && aOptions.mCompositionOp != CompositionOp::OP_CLEAR) {
3607 shadowColor = Some(aShadow->mColor);
3608 if (color) {
3609 shadowColor->a *= color->a;
3610 }
3611 }
3612 SamplingFilter filter =
3613 aShadow ? SamplingFilter::GOOD : GetSamplingFilter(aPattern);
3614 if (handle && handle->IsValid()) {
3615 // If the entry has a valid texture handle still, use it. However, the
3616 // entry texture is assumed to be located relative to its previous bounds.
3617 // We need to offset the pattern by the difference between its new unclipped
3618 // origin and its previous previous unclipped origin. Then when we finally
3619 // draw a rectangle at the expected new bounds, it will overlap the portion
3620 // of the old entry texture we actually need to sample from.
3621 Point offset =
3622 (realOrigin - entry->GetOrigin()) + entry->GetBounds().TopLeft();
3623 SurfacePattern pathPattern(nullptr, ExtendMode::CLAMP,
3624 Matrix::Translation(offset), filter);
3625 return DrawRectAccel(quantBounds, pathPattern, aOptions, shadowColor,
3626 &handle, false, true, true);
3627 }
3628
3629 if (mPathVertexCapacity > 0 && !handle && entry && !aShadow &&
3630 aOptions.mAntialiasMode != AntialiasMode::NONE &&
3631 SupportsPattern(aPattern) &&
3632 entry->GetPath().mPath.num_types <= mPathMaxComplexity) {
3633 if (entry->GetVertexRange().IsValid()) {
3634 // If there is a valid cached vertex data in the path vertex buffer, then
3635 // just draw that. We must draw at integer pixel boundaries (using
3636 // intBounds instead of quantBounds) due to WGR's reliance on pixel center
3637 // location.
3638 mCurrentTarget->mProfile.OnCacheHit();
3639 return DrawRectAccel(Rect(intBounds.TopLeft(), Size(1, 1)), aPattern,
3640 aOptions, Nothing(), nullptr, false, true, true,
3641 false, nullptr, &entry->GetVertexRange());
3642 }
3643
3644 // printf_stderr("Generating... verbs %d, points %d\n",
3645 // int(pathSkia->GetPath().countVerbs()),
3646 // int(pathSkia->GetPath().countPoints()));
3647 WGR::OutputVertex* outputBuffer = nullptr;
3648 size_t outputBufferCapacity = 0;
3649 if (mWGROutputBuffer) {
3650 outputBuffer = mWGROutputBuffer.get();
3651 outputBufferCapacity = mPathVertexCapacity / sizeof(WGR::OutputVertex);
3652 }
3653 Maybe<WGR::VertexBuffer> wgrVB;
3654 Maybe<AAStroke::VertexBuffer> strokeVB;
3655 if (!aStrokeOptions) {
3656 if (aPath == mUnitCirclePath) {
3657 auto scaleFactors = pathXform.ScaleFactors();
3658 if (scaleFactors.AreScalesSame()) {
3659 Point center = pathXform.GetTranslation() - quantBounds.TopLeft();
3660 float radius = scaleFactors.xScale;
3661 AAStroke::VertexBuffer vb = AAStroke::aa_stroke_filled_circle(
3662 center.x, center.y, radius, (AAStroke::OutputVertex*)outputBuffer,
3663 outputBufferCapacity);
3664 if (!vb.len || (outputBuffer && vb.len > outputBufferCapacity)) {
3665 AAStroke::aa_stroke_vertex_buffer_release(vb);
3666 } else {
3667 strokeVB = Some(vb);
3668 }
3669 }
3670 }
3671 if (!strokeVB) {
3672 wgrVB = GeneratePathVertexBuffer(
3673 entry->GetPath(), IntRect(-intBounds.TopLeft(), mViewportSize),
3674 mRasterizationTruncates, outputBuffer, outputBufferCapacity);
3675 }
3676 } else {
3677 if (aaStrokeMode != AAStrokeMode::Unsupported) {
3678 auto scaleFactors = currentTransform.ScaleFactors();
3679 if (scaleFactors.AreScalesSame()) {
3680 strokeVB = GenerateStrokeVertexBuffer(
3681 entry->GetPath(), aStrokeOptions, scaleFactors.xScale,
3682 outputBuffer, outputBufferCapacity);
3683 }
3684 }
3685 if (!strokeVB && mPathWGRStroke) {
3686 // If stroking, then generate a path to fill the stroked region. This
3687 // path will need to be quantized again because it differs from the
3688 // path used for the cache entry, but this allows us to avoid
3689 // generating a fill path on a cache hit.
3690 Maybe<Rect> cullRect;
3691 Matrix invTransform = currentTransform;
3692 if (invTransform.Invert()) {
3693 // Transform the stroking clip rect from device space to local
3694 // space.
3695 Rect invRect = invTransform.TransformBounds(Rect(mClipRect));
3696 invRect.RoundOut();
3697 cullRect = Some(invRect);
3698 }
3699 SkPath fillPath;
3700 if (pathSkia->GetFillPath(*aStrokeOptions, pathXform, fillPath,
3701 cullRect)) {
3702 // printf_stderr(" stroke fill... verbs %d, points %d\n",
3703 // int(fillPath.countVerbs()),
3704 // int(fillPath.countPoints()));
3705 if (Maybe<QuantizedPath> qp = GenerateQuantizedPath(
3706 mWGRPathBuilder, fillPath, quantBounds, pathXform)) {
3707 wgrVB = GeneratePathVertexBuffer(
3708 *qp, IntRect(-intBounds.TopLeft(), mViewportSize),
3709 mRasterizationTruncates, outputBuffer, outputBufferCapacity);
3710 }
3711 }
3712 }
3713 }
3714 if (wgrVB || strokeVB) {
3715 const uint8_t* vbData =
3716 wgrVB ? (const uint8_t*)wgrVB->data : (const uint8_t*)strokeVB->data;
3717 if (outputBuffer && !vbData) {
3718 vbData = (const uint8_t*)outputBuffer;
3719 }
3720 size_t vbLen = wgrVB ? wgrVB->len : strokeVB->len;
3721 uint32_t vertexBytes = uint32_t(
3722 std::min(vbLen * sizeof(WGR::OutputVertex), size_t(UINT32_MAX(4294967295U))));
3723 // printf_stderr(" ... %d verts, %d bytes\n", int(vbLen),
3724 // int(vertexBytes));
3725 if (vertexBytes > mPathVertexCapacity - mPathVertexOffset &&
3726 vertexBytes <= mPathVertexCapacity - sizeof(kRectVertexData)) {
3727 // If the vertex data is too large to fit in the remaining path vertex
3728 // buffer, then orphan the contents of the vertex buffer to make room
3729 // for it.
3730 if (mPathCache) {
3731 mPathCache->ClearVertexRanges();
3732 }
3733 ResetPathVertexBuffer();
3734 }
3735 if (vertexBytes <= mPathVertexCapacity - mPathVertexOffset) {
3736 // If there is actually room to fit the vertex data in the vertex buffer
3737 // after orphaning as necessary, then upload the data to the next
3738 // available offset in the buffer.
3739 PathVertexRange vertexRange(
3740 uint32_t(mPathVertexOffset / sizeof(WGR::OutputVertex)),
3741 uint32_t(vbLen));
3742 // printf_stderr(" ... offset %d\n", mPathVertexOffset);
3743 // Normal glBufferSubData interleaved with draw calls causes performance
3744 // issues on Mali, so use our special unsynchronized version. This is
3745 // safe as we never update regions referenced by pending draw calls.
3746 mWebgl->BufferSubData(LOCAL_GL_ARRAY_BUFFER0x8892, mPathVertexOffset,
3747 vertexBytes, vbData,
3748 /* unsynchronized */ true);
3749 mPathVertexOffset += vertexBytes;
3750 if (wgrVB) {
3751 WGR::wgr_vertex_buffer_release(wgrVB.ref());
3752 } else {
3753 AAStroke::aa_stroke_vertex_buffer_release(strokeVB.ref());
3754 }
3755 if (strokeVB && aaStrokeMode == AAStrokeMode::Mask) {
3756 // Attempt to generate a stroke mask for path.
3757 if (RefPtr<TextureHandle> handle =
3758 DrawStrokeMask(vertexRange, intBounds.Size())) {
3759 // Finally, draw the rendered stroke mask.
3760 if (entry) {
3761 entry->Link(handle);
3762 }
3763 mCurrentTarget->mProfile.OnCacheMiss();
3764 SurfacePattern maskPattern(
3765 nullptr, ExtendMode::CLAMP,
3766 Matrix::Translation(quantBounds.TopLeft()),
3767 SamplingFilter::GOOD);
3768 return DrawRectAccel(quantBounds, maskPattern, aOptions, color,
3769 &handle, false, true, true);
3770 }
3771 } else {
3772 // Remember the vertex range in the cache entry so that it can be
3773 // reused later.
3774 if (entry) {
3775 entry->SetVertexRange(vertexRange);
3776 }
3777
3778 // Finally, draw the uploaded vertex data.
3779 mCurrentTarget->mProfile.OnCacheMiss();
3780 return DrawRectAccel(Rect(intBounds.TopLeft(), Size(1, 1)), aPattern,
3781 aOptions, Nothing(), nullptr, false, true, true,
3782 false, nullptr, &vertexRange);
3783 }
3784 } else {
3785 if (wgrVB) {
3786 WGR::wgr_vertex_buffer_release(wgrVB.ref());
3787 } else {
3788 AAStroke::aa_stroke_vertex_buffer_release(strokeVB.ref());
3789 }
3790 }
3791 // If we failed to draw the vertex data for some reason, then fall through
3792 // to the texture rasterization path.
3793 }
3794 }
3795
3796 // If a stroke path covers too much screen area, it is likely that most is
3797 // empty space in the interior. This usually imposes too high a cost versus
3798 // just rasterizing without acceleration. Note that AA-Stroke generally
3799 // produces more acceptable amounts of geometry for larger paths, so we do
3800 // this heuristic after we attempt AA-Stroke.
3801 if (aStrokeOptions &&
3802 intBounds.width * intBounds.height >
3803 (mViewportSize.width / 2) * (mViewportSize.height / 2)) {
3804 // Avoid filling cache with empty entries.
3805 if (entry) {
3806 entry->Unlink();
3807 }
3808 return false;
3809 }
3810
3811 // If there isn't a valid texture handle, then we need to rasterize the
3812 // path in a software canvas and upload this to a texture. Solid color
3813 // patterns will be rendered as a path mask that can then be modulated
3814 // with any color. Other pattern types have to rasterize the pattern
3815 // directly into the cached texture.
3816 handle = nullptr;
3817 RefPtr<DrawTargetSkia> pathDT = new DrawTargetSkia;
3818 if (pathDT->Init(intBounds.Size(), color || aShadow
3819 ? SurfaceFormat::A8
3820 : SurfaceFormat::B8G8R8A8)) {
3821 Point offset = -quantBounds.TopLeft();
3822 if (aShadow) {
3823 // Ensure the the shadow is drawn at the requested offset
3824 offset += aShadow->mOffset;
3825 }
3826 DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER,
3827 aOptions.mAntialiasMode);
3828 static const ColorPattern maskPattern(DeviceColor(1.0f, 1.0f, 1.0f, 1.0f));
3829 const Pattern& cachePattern = color ? maskPattern : aPattern;
3830 // If the source pattern is a DrawTargetWebgl snapshot, we may shift
3831 // targets when drawing the path, so back up the old target.
3832 DrawTargetWebgl* oldTarget = mCurrentTarget;
3833 {
3834 RefPtr<const Path> path;
3835 if (!aPathXform || (color && !aStrokeOptions)) {
3836 // If the pattern is transform invariant or there is no pathXform, then
3837 // it is safe to use the path directly. Solid colors are transform
3838 // invariant, except when there are stroke options such as line width or
3839 // dashes that should not be scaled by pathXform.
3840 path = aPath;
3841 pathDT->SetTransform(pathXform * Matrix::Translation(offset));
3842 } else {
3843 // If there is a pathXform, then pre-apply that to the path to avoid
3844 // altering the pattern.
3845 RefPtr<PathBuilder> builder =
3846 aPath->TransformedCopyToBuilder(*aPathXform);
3847 path = builder->Finish();
3848 pathDT->SetTransform(currentTransform * Matrix::Translation(offset));
3849 }
3850 if (aStrokeOptions) {
3851 pathDT->Stroke(path, cachePattern, *aStrokeOptions, drawOptions);
3852 } else {
3853 pathDT->Fill(path, cachePattern, drawOptions);
3854 }
3855 }
3856 if (aShadow && aShadow->mSigma > 0.0f) {
3857 // Blur the shadow if required.
3858 uint8_t* data = nullptr;
3859 IntSize size;
3860 int32_t stride = 0;
3861 SurfaceFormat format = SurfaceFormat::UNKNOWN;
3862 if (pathDT->LockBits(&data, &size, &stride, &format)) {
3863 AlphaBoxBlur blur(Rect(pathDT->GetRect()), stride, aShadow->mSigma,
3864 aShadow->mSigma);
3865 blur.Blur(data);
3866 pathDT->ReleaseBits(data);
3867 }
3868 }
3869 RefPtr<SourceSurface> pathSurface = pathDT->Snapshot();
3870 // If the target changed, try to restore it.
3871 if (pathSurface &&
3872 (mCurrentTarget == oldTarget || oldTarget->PrepareContext())) {
3873 SurfacePattern pathPattern(pathSurface, ExtendMode::CLAMP,
3874 Matrix::Translation(quantBounds.TopLeft()),
3875 filter);
3876 // Try and upload the rasterized path to a texture. If there is a
3877 // valid texture handle after this, then link it to the entry.
3878 // Otherwise, we might have to fall back to software drawing the
3879 // path, so unlink it from the entry.
3880 if (DrawRectAccel(quantBounds, pathPattern, aOptions, shadowColor,
3881 &handle, false, true) &&
3882 handle) {
3883 if (entry) {
3884 entry->Link(handle);
3885 }
3886 } else if (entry) {
3887 entry->Unlink();
3888 }
3889 return true;
3890 }
3891 }
3892
3893 // Avoid filling cache with empty entries.
3894 if (entry) {
3895 entry->Unlink();
3896 }
3897 return false;
3898}
3899
3900void DrawTargetWebgl::DrawPath(const Path* aPath, const Pattern& aPattern,
3901 const DrawOptions& aOptions,
3902 const StrokeOptions* aStrokeOptions,
3903 bool aAllowStrokeAlpha) {
3904 // If there is a WebGL context, then try to cache the path to avoid slow
3905 // fallbacks.
3906 if (ShouldAccelPath(aOptions, aStrokeOptions) &&
3907 mSharedContext->DrawPathAccel(aPath, aPattern, aOptions, aStrokeOptions,
3908 aAllowStrokeAlpha)) {
3909 return;
3910 }
3911
3912 // There was no path cache entry available to use, so fall back to drawing the
3913 // path with Skia.
3914 MarkSkiaChanged(aOptions);
3915 if (aStrokeOptions) {
3916 mSkia->Stroke(aPath, aPattern, *aStrokeOptions, aOptions);
3917 } else {
3918 mSkia->Fill(aPath, aPattern, aOptions);
3919 }
3920}
3921
3922// DrawCircleAccel is a more specialized version of DrawPathAccel that attempts
3923// to cache a unit circle.
3924bool SharedContextWebgl::DrawCircleAccel(const Point& aCenter, float aRadius,
3925 const Pattern& aPattern,
3926 const DrawOptions& aOptions,
3927 const StrokeOptions* aStrokeOptions) {
3928 // Cache a unit circle and transform it to avoid creating a path repeatedly.
3929 if (!mUnitCirclePath) {
3930 mUnitCirclePath = MakePathForCircle(*mCurrentTarget, Point(0, 0), 1);
3931 }
3932 // Scale and translate the circle to the desired shape.
3933 Matrix circleXform(aRadius, 0, 0, aRadius, aCenter.x, aCenter.y);
3934 return DrawPathAccel(mUnitCirclePath, aPattern, aOptions, aStrokeOptions,
3935 true, nullptr, true, &circleXform);
3936}
3937
3938void DrawTargetWebgl::DrawCircle(const Point& aOrigin, float aRadius,
3939 const Pattern& aPattern,
3940 const DrawOptions& aOptions,
3941 const StrokeOptions* aStrokeOptions) {
3942 if (ShouldAccelPath(aOptions, aStrokeOptions) &&
3943 mSharedContext->DrawCircleAccel(aOrigin, aRadius, aPattern, aOptions,
3944 aStrokeOptions)) {
3945 return;
3946 }
3947
3948 MarkSkiaChanged(aOptions);
3949 if (aStrokeOptions) {
3950 mSkia->StrokeCircle(aOrigin, aRadius, aPattern, *aStrokeOptions, aOptions);
3951 } else {
3952 mSkia->FillCircle(aOrigin, aRadius, aPattern, aOptions);
3953 }
3954}
3955
3956void DrawTargetWebgl::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
3957 const Rect& aSource,
3958 const DrawSurfaceOptions& aSurfOptions,
3959 const DrawOptions& aOptions) {
3960 Matrix matrix = Matrix::Scaling(aDest.width / aSource.width,
3961 aDest.height / aSource.height);
3962 matrix.PreTranslate(-aSource.TopLeft());
3963 matrix.PostTranslate(aDest.TopLeft());
3964
3965 // Ensure the source rect is clipped to the surface bounds.
3966 Rect src = aSource.Intersect(Rect(aSurface->GetRect()));
3967 // Ensure the destination rect does not sample outside the surface bounds.
3968 Rect dest = matrix.TransformBounds(src).Intersect(aDest);
3969 SurfacePattern pattern(aSurface, ExtendMode::CLAMP, matrix,
3970 aSurfOptions.mSamplingFilter);
3971 DrawRect(dest, pattern, aOptions);
3972}
3973
3974void DrawTargetWebgl::Mask(const Pattern& aSource, const Pattern& aMask,
3975 const DrawOptions& aOptions) {
3976 if (!SupportsDrawOptions(aOptions) ||
3977 aMask.GetType() != PatternType::SURFACE ||
3978 aSource.GetType() != PatternType::COLOR) {
3979 MarkSkiaChanged(aOptions);
3980 mSkia->Mask(aSource, aMask, aOptions);
3981 return;
3982 }
3983 auto sourceColor = static_cast<const ColorPattern&>(aSource).mColor;
3984 auto maskPattern = static_cast<const SurfacePattern&>(aMask);
3985 DrawRect(Rect(IntRect(IntPoint(), maskPattern.mSurface->GetSize())),
3986 maskPattern, aOptions, Some(sourceColor));
3987}
3988
3989void DrawTargetWebgl::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
3990 Point aOffset, const DrawOptions& aOptions) {
3991 if (!SupportsDrawOptions(aOptions) ||
3992 aSource.GetType() != PatternType::COLOR) {
3993 MarkSkiaChanged(aOptions);
3994 mSkia->MaskSurface(aSource, aMask, aOffset, aOptions);
3995 } else {
3996 auto sourceColor = static_cast<const ColorPattern&>(aSource).mColor;
3997 SurfacePattern pattern(
3998 aMask, ExtendMode::CLAMP,
3999 Matrix::Translation(aOffset + aMask->GetRect().TopLeft()));
4000 DrawRect(Rect(aOffset, Size(aMask->GetSize())), pattern, aOptions,
4001 Some(sourceColor));
4002 }
4003}
4004
4005// Extract the surface's alpha values into an A8 surface.
4006static already_AddRefed<DataSourceSurface> ExtractAlpha(SourceSurface* aSurface,
4007 bool aAllowSubpixelAA) {
4008 RefPtr<DataSourceSurface> surfaceData = aSurface->GetDataSurface();
4009 if (!surfaceData) {
4010 return nullptr;
4011 }
4012 DataSourceSurface::ScopedMap srcMap(surfaceData, DataSourceSurface::READ);
4013 if (!srcMap.IsMapped()) {
4014 return nullptr;
4015 }
4016 IntSize size = surfaceData->GetSize();
4017 RefPtr<DataSourceSurface> alpha =
4018 Factory::CreateDataSourceSurface(size, SurfaceFormat::A8, false);
4019 if (!alpha) {
4020 return nullptr;
4021 }
4022 DataSourceSurface::ScopedMap dstMap(alpha, DataSourceSurface::WRITE);
4023 if (!dstMap.IsMapped()) {
4024 return nullptr;
4025 }
4026 // For subpixel masks, ignore the alpha and instead sample one of the color
4027 // channels as if they were alpha.
4028 SwizzleData(
4029 srcMap.GetData(), srcMap.GetStride(),
4030 aAllowSubpixelAA ? SurfaceFormat::A8R8G8B8 : surfaceData->GetFormat(),
4031 dstMap.GetData(), dstMap.GetStride(), SurfaceFormat::A8, size);
4032 return alpha.forget();
4033}
4034
4035void DrawTargetWebgl::DrawShadow(const Path* aPath, const Pattern& aPattern,
4036 const ShadowOptions& aShadow,
4037 const DrawOptions& aOptions,
4038 const StrokeOptions* aStrokeOptions) {
4039 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
4040 return;
4041 }
4042
4043 // If there is a WebGL context, then try to cache the path to avoid slow
4044 // fallbacks.
4045 if (ShouldAccelPath(aOptions, aStrokeOptions) &&
4046 mSharedContext->DrawPathAccel(aPath, aPattern, aOptions, aStrokeOptions,
4047 false, &aShadow)) {
4048 return;
4049 }
4050
4051 // There was no path cache entry available to use, so fall back to drawing the
4052 // path with Skia.
4053 MarkSkiaChanged(aOptions);
4054 mSkia->DrawShadow(aPath, aPattern, aShadow, aOptions, aStrokeOptions);
4055}
4056
4057void DrawTargetWebgl::DrawSurfaceWithShadow(SourceSurface* aSurface,
4058 const Point& aDest,
4059 const ShadowOptions& aShadow,
4060 CompositionOp aOperator) {
4061 DrawOptions options(1.0f, aOperator);
4062 if (ShouldAccelPath(options, nullptr)) {
4063 SurfacePattern pattern(aSurface, ExtendMode::CLAMP,
4064 Matrix::Translation(aDest));
4065 SkPath skiaPath;
4066 skiaPath.addRect(RectToSkRect(Rect(aSurface->GetRect()) + aDest));
4067 RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
4068 AutoRestoreTransform restore(this);
4069 SetTransform(Matrix());
4070 if (mSharedContext->DrawPathAccel(path, pattern, options, nullptr, false,
4071 &aShadow, false)) {
4072 DrawRect(Rect(aSurface->GetRect()) + aDest, pattern, options);
4073 return;
4074 }
4075 }
4076
4077 MarkSkiaChanged(options);
4078 mSkia->DrawSurfaceWithShadow(aSurface, aDest, aShadow, aOperator);
4079}
4080
4081already_AddRefed<PathBuilder> DrawTargetWebgl::CreatePathBuilder(
4082 FillRule aFillRule) const {
4083 return mSkia->CreatePathBuilder(aFillRule);
4084}
4085
4086void DrawTargetWebgl::SetTransform(const Matrix& aTransform) {
4087 DrawTarget::SetTransform(aTransform);
4088 mSkia->SetTransform(aTransform);
4089}
4090
4091void DrawTargetWebgl::StrokeRect(const Rect& aRect, const Pattern& aPattern,
4092 const StrokeOptions& aStrokeOptions,
4093 const DrawOptions& aOptions) {
4094 if (!mWebglValid) {
4095 MarkSkiaChanged(aOptions);
4096 mSkia->StrokeRect(aRect, aPattern, aStrokeOptions, aOptions);
4097 } else {
4098 // If the stroke options are unsupported, then transform the rect to a path
4099 // so it can be cached.
4100 SkPath skiaPath;
4101 skiaPath.addRect(RectToSkRect(aRect));
4102 RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
4103 DrawPath(path, aPattern, aOptions, &aStrokeOptions, true);
4104 }
4105}
4106
4107static inline bool IsThinLine(const Matrix& aTransform,
4108 const StrokeOptions& aStrokeOptions) {
4109 auto scale = aTransform.ScaleFactors();
4110 return std::max(scale.xScale, scale.yScale) * aStrokeOptions.mLineWidth <= 1;
4111}
4112
4113bool DrawTargetWebgl::StrokeLineAccel(const Point& aStart, const Point& aEnd,
4114 const Pattern& aPattern,
4115 const StrokeOptions& aStrokeOptions,
4116 const DrawOptions& aOptions,
4117 bool aClosed) {
4118 // Approximating a wide line as a rectangle works only with certain cap styles
4119 // in the general case (butt or square). However, if the line width is
4120 // sufficiently thin, we can either ignore the round cap (or treat it like
4121 // square for zero-length lines) without causing objectionable artifacts.
4122 // Lines may sometimes be used in closed paths that immediately reverse back,
4123 // in which case we need to use mLineJoin instead of mLineCap to determine the
4124 // actual cap used.
4125 CapStyle capStyle =
4126 aClosed ? (aStrokeOptions.mLineJoin == JoinStyle::ROUND ? CapStyle::ROUND
4127 : CapStyle::BUTT)
4128 : aStrokeOptions.mLineCap;
4129 if (mWebglValid && SupportsPattern(aPattern) &&
4130 (capStyle != CapStyle::ROUND ||
4131 IsThinLine(GetTransform(), aStrokeOptions)) &&
4132 aStrokeOptions.mDashPattern == nullptr && aStrokeOptions.mLineWidth > 0) {
4133 // Treat the line as a rectangle whose center-line is the supplied line and
4134 // for which the height is the supplied line width. Generate a matrix that
4135 // maps the X axis to the orientation of the line and the Y axis to the
4136 // normal vector to the line. This only works if the line caps are squared,
4137 // as rounded rectangles are currently not supported for round line caps.
4138 Point start = aStart;
4139 Point dirX = aEnd - aStart;
4140 Point dirY;
4141 float dirLen = dirX.Length();
4142 float scale = aStrokeOptions.mLineWidth;
4143 if (dirLen == 0.0f) {
4144 // If the line is zero-length, then only a cap is rendered.
4145 switch (capStyle) {
4146 case CapStyle::BUTT:
4147 // The cap doesn't extend beyond the line so nothing is drawn.
4148 return true;
4149 case CapStyle::ROUND:
4150 case CapStyle::SQUARE:
4151 // Draw a unit square centered at the single point.
4152 dirX = Point(scale, 0.0f);
4153 dirY = Point(0.0f, scale);
4154 // Offset the start by half a unit.
4155 start.x -= 0.5f * scale;
4156 break;
4157 }
4158 } else {
4159 // Make the scale map to a single unit length.
4160 scale /= dirLen;
4161 dirY = Point(-dirX.y, dirX.x) * scale;
4162 if (capStyle == CapStyle::SQUARE) {
4163 // Offset the start by half a unit.
4164 start -= (dirX * scale) * 0.5f;
4165 // Ensure the extent also accounts for the start and end cap.
4166 dirX += dirX * scale;
4167 }
4168 }
4169 Matrix lineXform(dirX.x, dirX.y, dirY.x, dirY.y, start.x - 0.5f * dirY.x,
4170 start.y - 0.5f * dirY.y);
4171 if (PrepareContext() &&
4172 mSharedContext->DrawRectAccel(Rect(0, 0, 1, 1), aPattern, aOptions,
4173 Nothing(), nullptr, true, true, true,
4174 false, nullptr, nullptr, &lineXform)) {
4175 return true;
4176 }
4177 }
4178 return false;
4179}
4180
4181void DrawTargetWebgl::StrokeLine(const Point& aStart, const Point& aEnd,
4182 const Pattern& aPattern,
4183 const StrokeOptions& aStrokeOptions,
4184 const DrawOptions& aOptions) {
4185 if (!mWebglValid) {
4186 MarkSkiaChanged(aOptions);
4187 mSkia->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
4188 } else if (!StrokeLineAccel(aStart, aEnd, aPattern, aStrokeOptions,
4189 aOptions)) {
4190 // If the stroke options are unsupported, then transform the line to a path
4191 // so it can be cached.
4192 SkPath skiaPath;
4193 skiaPath.moveTo(PointToSkPoint(aStart));
4194 skiaPath.lineTo(PointToSkPoint(aEnd));
4195 RefPtr<PathSkia> path = new PathSkia(skiaPath, FillRule::FILL_WINDING);
4196 DrawPath(path, aPattern, aOptions, &aStrokeOptions, true);
4197 }
4198}
4199
4200void DrawTargetWebgl::Stroke(const Path* aPath, const Pattern& aPattern,
4201 const StrokeOptions& aStrokeOptions,
4202 const DrawOptions& aOptions) {
4203 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
4204 return;
4205 }
4206 const auto& skiaPath = static_cast<const PathSkia*>(aPath)->GetPath();
4207 if (!mWebglValid) {
4208 MarkSkiaChanged(aOptions);
4209 mSkia->Stroke(aPath, aPattern, aStrokeOptions, aOptions);
4210 return;
4211 }
4212
4213 // Avoid using Skia's isLine here because some paths erroneously include a
4214 // closePath at the end, causing isLine to not detect the line. In that case
4215 // we just draw a line in reverse right over the original line.
4216 int numVerbs = skiaPath.countVerbs();
4217 bool allowStrokeAlpha = false;
4218 if (numVerbs >= 2 && numVerbs <= 3) {
4219 uint8_t verbs[3];
4220 skiaPath.getVerbs(verbs, numVerbs);
4221 if (verbs[0] == SkPath::kMove_Verb && verbs[1] == SkPath::kLine_Verb &&
4222 (numVerbs < 3 || verbs[2] == SkPath::kClose_Verb)) {
4223 bool closed = numVerbs >= 3;
4224 Point start = SkPointToPoint(skiaPath.getPoint(0));
4225 Point end = SkPointToPoint(skiaPath.getPoint(1));
4226 if (StrokeLineAccel(start, end, aPattern, aStrokeOptions, aOptions,
4227 closed)) {
4228 if (closed) {
4229 StrokeLineAccel(end, start, aPattern, aStrokeOptions, aOptions, true);
4230 }
4231 return;
4232 }
4233 // If accelerated line drawing failed, just treat it as a path.
4234 allowStrokeAlpha = true;
4235 }
4236 }
4237
4238 DrawPath(aPath, aPattern, aOptions, &aStrokeOptions, allowStrokeAlpha);
4239}
4240
4241void DrawTargetWebgl::StrokeCircle(const Point& aOrigin, float aRadius,
4242 const Pattern& aPattern,
4243 const StrokeOptions& aStrokeOptions,
4244 const DrawOptions& aOptions) {
4245 DrawCircle(aOrigin, aRadius, aPattern, aOptions, &aStrokeOptions);
4246}
4247
4248bool DrawTargetWebgl::ShouldUseSubpixelAA(ScaledFont* aFont,
4249 const DrawOptions& aOptions) {
4250 AntialiasMode aaMode = aFont->GetDefaultAAMode();
4251 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
4252 aaMode = aOptions.mAntialiasMode;
4253 }
4254 return GetPermitSubpixelAA() &&
4255 (aaMode == AntialiasMode::DEFAULT ||
4256 aaMode == AntialiasMode::SUBPIXEL) &&
4257 aOptions.mCompositionOp == CompositionOp::OP_OVER;
4258}
4259
4260void DrawTargetWebgl::StrokeGlyphs(ScaledFont* aFont,
4261 const GlyphBuffer& aBuffer,
4262 const Pattern& aPattern,
4263 const StrokeOptions& aStrokeOptions,
4264 const DrawOptions& aOptions) {
4265 if (!aFont || !aBuffer.mNumGlyphs) {
4266 return;
4267 }
4268
4269 bool useSubpixelAA = ShouldUseSubpixelAA(aFont, aOptions);
4270
4271 if (mWebglValid && SupportsDrawOptions(aOptions) &&
4272 aPattern.GetType() == PatternType::COLOR && PrepareContext() &&
4273 mSharedContext->DrawGlyphsAccel(aFont, aBuffer, aPattern, aOptions,
4274 &aStrokeOptions, useSubpixelAA)) {
4275 return;
4276 }
4277
4278 if (useSubpixelAA) {
4279 // Subpixel AA does not support layering because the subpixel masks can't
4280 // blend with the over op.
4281 MarkSkiaChanged();
4282 } else {
4283 MarkSkiaChanged(aOptions);
4284 }
4285 mSkia->StrokeGlyphs(aFont, aBuffer, aPattern, aStrokeOptions, aOptions);
4286}
4287
4288// Depending on whether we enable subpixel position for a given font, Skia may
4289// round transformed coordinates differently on each axis. By default, text is
4290// subpixel quantized horizontally and snapped to a whole integer vertical
4291// baseline. Axis-flip transforms instead snap to horizontal boundaries while
4292// subpixel quantizing along the vertical. For other types of transforms, Skia
4293// just applies subpixel quantization to both axes.
4294// We must duplicate the amount of quantization Skia applies carefully as a
4295// boundary value such as 0.49 may round to 0.5 with subpixel quantization,
4296// but if Skia actually snapped it to a whole integer instead, it would round
4297// down to 0. If a subsequent glyph with offset 0.51 came in, we might
4298// mistakenly round it down to 0.5, whereas Skia would round it up to 1. Thus
4299// we would alias 0.49 and 0.51 to the same cache entry, while Skia would
4300// actually snap the offset to 0 or 1, depending, resulting in mismatched
4301// hinting.
4302static inline IntPoint QuantizeScale(ScaledFont* aFont,
4303 const Matrix& aTransform) {
4304 if (!aFont->UseSubpixelPosition()) {
4305 return {1, 1};
4306 }
4307 if (aTransform._12 == 0) {
4308 // Glyphs are rendered subpixel horizontally, so snap vertically.
4309 return {4, 1};
4310 }
4311 if (aTransform._11 == 0) {
4312 // Glyphs are rendered subpixel vertically, so snap horizontally.
4313 return {1, 4};
4314 }
4315 // The transform isn't aligned, so don't snap.
4316 return {4, 4};
4317}
4318
4319// Skia only supports subpixel positioning to the nearest 1/4 fraction. It
4320// would be wasteful to attempt to cache text runs with positioning that is
4321// anymore precise than this. To prevent this cache bloat, we quantize the
4322// transformed glyph positions to the nearest 1/4. The scaling factor for
4323// the quantization is baked into the transform, so that if subpixel rounding
4324// is used on a given axis, then the axis will be multiplied by 4 before
4325// rounding. Since the quantized position is not used for rasterization, the
4326// transform is safe to modify as such.
4327static inline IntPoint QuantizePosition(const Matrix& aTransform,
4328 const IntPoint& aOffset,
4329 const Point& aPosition) {
4330 return RoundedToInt(aTransform.TransformPoint(aPosition)) - aOffset;
4331}
4332
4333// Get a quantized starting offset for the glyph buffer. We want this offset
4334// to encapsulate the transform and buffer offset while still preserving the
4335// relative subpixel positions of the glyphs this offset is subtracted from.
4336static inline IntPoint QuantizeOffset(const Matrix& aTransform,
4337 const IntPoint& aQuantizeScale,
4338 const GlyphBuffer& aBuffer) {
4339 IntPoint offset =
4340 RoundedToInt(aTransform.TransformPoint(aBuffer.mGlyphs[0].mPosition));
4341 offset.x.value &= ~(aQuantizeScale.x.value - 1);
4342 offset.y.value &= ~(aQuantizeScale.y.value - 1);
4343 return offset;
4344}
4345
4346// Hashes a glyph buffer to a single hash value that can be used for quick
4347// comparisons. Each glyph position is transformed and quantized before
4348// hashing.
4349HashNumber GlyphCacheEntry::HashGlyphs(const GlyphBuffer& aBuffer,
4350 const Matrix& aTransform,
4351 const IntPoint& aQuantizeScale) {
4352 HashNumber hash = 0;
4353 IntPoint offset = QuantizeOffset(aTransform, aQuantizeScale, aBuffer);
4354 for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
4355 const Glyph& glyph = aBuffer.mGlyphs[i];
4356 hash = AddToHash(hash, glyph.mIndex);
4357 IntPoint pos = QuantizePosition(aTransform, offset, glyph.mPosition);
4358 hash = AddToHash(hash, pos.x);
4359 hash = AddToHash(hash, pos.y);
4360 }
4361 return hash;
4362}
4363
4364// Determines if an existing glyph cache entry matches an incoming text run.
4365inline bool GlyphCacheEntry::MatchesGlyphs(
4366 const GlyphBuffer& aBuffer, const DeviceColor& aColor,
4367 const Matrix& aTransform, const IntPoint& aQuantizeOffset,
4368 const IntPoint& aBoundsOffset, const IntRect& aClipRect, HashNumber aHash,
4369 const StrokeOptions* aStrokeOptions) {
4370 // First check if the hash matches to quickly reject the text run before any
4371 // more expensive checking. If it matches, then check if the color and
4372 // transform are the same.
4373 if (aHash != mHash || aBuffer.mNumGlyphs != mBuffer.mNumGlyphs ||
4374 aColor != mColor || !HasMatchingScale(aTransform, mTransform)) {
4375 return false;
4376 }
4377 // Finally check if all glyphs and their quantized positions match.
4378 for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
4379 const Glyph& dst = mBuffer.mGlyphs[i];
4380 const Glyph& src = aBuffer.mGlyphs[i];
4381 if (dst.mIndex != src.mIndex ||
4382 dst.mPosition != Point(QuantizePosition(aTransform, aQuantizeOffset,
4383 src.mPosition))) {
4384 return false;
4385 }
4386 }
4387 // Check that stroke options actually match.
4388 if (aStrokeOptions) {
4389 // If stroking, verify that the entry is also stroked with the same options.
4390 if (!(mStrokeOptions && *aStrokeOptions == *mStrokeOptions)) {
4391 return false;
4392 }
4393 } else if (mStrokeOptions) {
4394 // If not stroking, check if the entry is stroked. If so, don't match.
4395 return false;
4396 }
4397 // Verify that the full bounds, once translated and clipped, are equal to the
4398 // clipped bounds.
4399 return (mFullBounds + aBoundsOffset)
4400 .Intersect(aClipRect)
4401 .IsEqualEdges(GetBounds() + aBoundsOffset);
4402}
4403
4404GlyphCacheEntry::GlyphCacheEntry(const GlyphBuffer& aBuffer,
4405 const DeviceColor& aColor,
4406 const Matrix& aTransform,
4407 const IntPoint& aQuantizeScale,
4408 const IntRect& aBounds,
4409 const IntRect& aFullBounds, HashNumber aHash,
4410 StoredStrokeOptions* aStrokeOptions)
4411 : CacheEntryImpl<GlyphCacheEntry>(aTransform, aBounds, aHash),
4412 mColor(aColor),
4413 mFullBounds(aFullBounds),
4414 mStrokeOptions(aStrokeOptions) {
4415 // Store a copy of the glyph buffer with positions already quantized for fast
4416 // comparison later.
4417 Glyph* glyphs = new Glyph[aBuffer.mNumGlyphs];
4418 IntPoint offset = QuantizeOffset(aTransform, aQuantizeScale, aBuffer);
4419 // Make the bounds relative to the offset so we can add a new offset later.
4420 IntPoint boundsOffset(offset.x / aQuantizeScale.x,
4421 offset.y / aQuantizeScale.y);
4422 mBounds -= boundsOffset;
4423 mFullBounds -= boundsOffset;
4424 for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
4425 Glyph& dst = glyphs[i];
4426 const Glyph& src = aBuffer.mGlyphs[i];
4427 dst.mIndex = src.mIndex;
4428 dst.mPosition = Point(QuantizePosition(aTransform, offset, src.mPosition));
4429 }
4430 mBuffer.mGlyphs = glyphs;
4431 mBuffer.mNumGlyphs = aBuffer.mNumGlyphs;
4432}
4433
4434GlyphCacheEntry::~GlyphCacheEntry() { delete[] mBuffer.mGlyphs; }
4435
4436// Attempt to find a matching entry in the glyph cache. The caller should check
4437// whether the contained texture handle is valid to determine if it will need to
4438// render the text run or just reuse the cached texture.
4439already_AddRefed<GlyphCacheEntry> GlyphCache::FindEntry(
4440 const GlyphBuffer& aBuffer, const DeviceColor& aColor,
4441 const Matrix& aTransform, const IntPoint& aQuantizeScale,
4442 const IntRect& aClipRect, HashNumber aHash,
4443 const StrokeOptions* aStrokeOptions) {
4444 IntPoint offset = QuantizeOffset(aTransform, aQuantizeScale, aBuffer);
4445 IntPoint boundsOffset(offset.x / aQuantizeScale.x,
4446 offset.y / aQuantizeScale.y);
4447 for (const RefPtr<GlyphCacheEntry>& entry : GetChain(aHash)) {
4448 if (entry->MatchesGlyphs(aBuffer, aColor, aTransform, offset, boundsOffset,
4449 aClipRect, aHash, aStrokeOptions)) {
4450 return do_AddRef(entry);
4451 }
4452 }
4453 return nullptr;
4454}
4455
4456// Insert a new entry in the glyph cache.
4457already_AddRefed<GlyphCacheEntry> GlyphCache::InsertEntry(
4458 const GlyphBuffer& aBuffer, const DeviceColor& aColor,
4459 const Matrix& aTransform, const IntPoint& aQuantizeScale,
4460 const IntRect& aBounds, const IntRect& aFullBounds, HashNumber aHash,
4461 const StrokeOptions* aStrokeOptions) {
4462 StoredStrokeOptions* strokeOptions = nullptr;
4463 if (aStrokeOptions) {
4464 strokeOptions = aStrokeOptions->Clone();
4465 if (!strokeOptions) {
4466 return nullptr;
4467 }
4468 }
4469 RefPtr<GlyphCacheEntry> entry =
4470 new GlyphCacheEntry(aBuffer, aColor, aTransform, aQuantizeScale, aBounds,
4471 aFullBounds, aHash, strokeOptions);
4472 Insert(entry);
4473 return entry.forget();
4474}
4475
4476GlyphCache::GlyphCache(ScaledFont* aFont) : mFont(aFont) {}
4477
4478static void ReleaseGlyphCache(void* aPtr) {
4479 delete static_cast<GlyphCache*>(aPtr);
1
Calling implicit destructor for 'GlyphCache'
2
Calling '~CacheImpl'
4480}
4481
4482// Whether all glyphs in the buffer match the last whitespace glyph queried.
4483bool GlyphCache::IsWhitespace(const GlyphBuffer& aBuffer) const {
4484 if (!mLastWhitespace) {
4485 return false;
4486 }
4487 uint32_t whitespace = *mLastWhitespace;
4488 for (size_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
4489 if (aBuffer.mGlyphs[i].mIndex != whitespace) {
4490 return false;
4491 }
4492 }
4493 return true;
4494}
4495
4496// Remember the last whitespace glyph seen.
4497void GlyphCache::SetLastWhitespace(const GlyphBuffer& aBuffer) {
4498 mLastWhitespace = Some(aBuffer.mGlyphs[0].mIndex);
4499}
4500
4501void DrawTargetWebgl::SetPermitSubpixelAA(bool aPermitSubpixelAA) {
4502 DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
4503 mSkia->SetPermitSubpixelAA(aPermitSubpixelAA);
4504}
4505
4506// Check for any color glyphs contained within a rasterized BGRA8 text result.
4507static bool CheckForColorGlyphs(const RefPtr<SourceSurface>& aSurface) {
4508 if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
4509 return false;
4510 }
4511 RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
4512 if (!dataSurf) {
4513 return true;
4514 }
4515 DataSourceSurface::ScopedMap map(dataSurf, DataSourceSurface::READ);
4516 if (!map.IsMapped()) {
4517 return true;
4518 }
4519 IntSize size = dataSurf->GetSize();
4520 const uint8_t* data = map.GetData();
4521 int32_t stride = map.GetStride();
4522 for (int y = 0; y < size.height; y++) {
4523 const uint32_t* x = (const uint32_t*)data;
4524 const uint32_t* end = x + size.width;
4525 for (; x < end; x++) {
4526 // Verify if all components are the same as for premultiplied grayscale.
4527 uint32_t color = *x;
4528 uint32_t gray = color & 0xFF;
4529 gray |= gray << 8;
4530 gray |= gray << 16;
4531 if (color != gray) return true;
4532 }
4533 data += stride;
4534 }
4535 return false;
4536}
4537
4538// Quantize the preblend color used to key the cache, as only the high bits are
4539// used to determine the amount of preblending. This avoids excessive cache use.
4540// This roughly matches the quantization used in WebRender and Skia.
4541static DeviceColor QuantizePreblendColor(const DeviceColor& aColor,
4542 bool aUseSubpixelAA) {
4543 int32_t r = int32_t(aColor.r * 255.0f + 0.5f);
4544 int32_t g = int32_t(aColor.g * 255.0f + 0.5f);
4545 int32_t b = int32_t(aColor.b * 255.0f + 0.5f);
4546 // Skia only uses the high 3 bits of each color component to cache preblend
4547 // ramp tables.
4548 constexpr int32_t lumBits = 3;
4549 constexpr int32_t floorMask = ((1 << lumBits) - 1) << (8 - lumBits);
4550 if (!aUseSubpixelAA) {
4551 // If not using subpixel AA, then quantize only the luminance, stored in the
4552 // G channel.
4553 g = (r * 54 + g * 183 + b * 19) >> 8;
4554 g &= floorMask;
4555 r = g;
4556 b = g;
4557 } else {
4558 r &= floorMask;
4559 g &= floorMask;
4560 b &= floorMask;
4561 }
4562 return DeviceColor{r / 255.0f, g / 255.0f, b / 255.0f, 1.0f};
4563}
4564
4565// Draws glyphs to the WebGL target by trying to generate a cached texture for
4566// the text run that can be subsequently reused to quickly render the text run
4567// without using any software surfaces.
4568bool SharedContextWebgl::DrawGlyphsAccel(ScaledFont* aFont,
4569 const GlyphBuffer& aBuffer,
4570 const Pattern& aPattern,
4571 const DrawOptions& aOptions,
4572 const StrokeOptions* aStrokeOptions,
4573 bool aUseSubpixelAA) {
4574 // Look for an existing glyph cache on the font. If not there, create it.
4575 GlyphCache* cache =
4576 static_cast<GlyphCache*>(aFont->GetUserData(&mGlyphCacheKey));
4577 if (!cache) {
4578 cache = new GlyphCache(aFont);
4579 aFont->AddUserData(&mGlyphCacheKey, cache, ReleaseGlyphCache);
4580 mGlyphCaches.insertFront(cache);
4581 }
4582
4583 // Check if the buffer contains non-renderable whitespace characters before
4584 // any other expensive checks.
4585 if (cache->IsWhitespace(aBuffer)) {
4586 return true;
4587 }
4588
4589 // Whether the font may use bitmaps. If so, we need to render the glyphs with
4590 // color as grayscale bitmaps will use the color while color emoji will not,
4591 // with no easy way to know ahead of time. We currently have to check the
4592 // rasterized result to see if there are any color glyphs. To render subpixel
4593 // masks, we need to know that the rasterized result actually represents a
4594 // subpixel mask rather than try to interpret it as a normal RGBA result such
4595 // as for color emoji.
4596 bool useBitmaps = !aStrokeOptions && aFont->MayUseBitmaps() &&
4597 aOptions.mCompositionOp != CompositionOp::OP_CLEAR;
4598 // Hash the incoming text run and looking for a matching entry.
4599 DeviceColor color = aOptions.mCompositionOp == CompositionOp::OP_CLEAR
4600 ? DeviceColor(1, 1, 1, 1)
4601 : static_cast<const ColorPattern&>(aPattern).mColor;
4602#if defined(XP_MACOSX)
4603 // macOS uses gamma-aware blending with font smooth from subpixel AA.
4604 // If font smoothing is requested, even if there is no subpixel AA, gamma-
4605 // aware blending might be used and differing amounts of dilation might be
4606 // applied.
4607 bool usePreblend = aUseSubpixelAA ||
4608 (aFont->GetType() == FontType::MAC &&
4609 static_cast<ScaledFontMac*>(aFont)->UseFontSmoothing());
4610#elif defined(XP_WIN)
4611 // Windows uses gamma-aware blending via ClearType with grayscale and subpixel
4612 // AA.
4613 bool usePreblend =
4614 aUseSubpixelAA || aOptions.mAntialiasMode != AntialiasMode::NONE;
4615#else
4616 // FreeType backends currently don't use any preblending.
4617 bool usePreblend = false;
4618#endif
4619
4620 // If the font has bitmaps, use the color directly. Otherwise, the texture
4621 // holds a grayscale mask, so encode the key's subpixel state in the color.
4622 const Matrix& currentTransform = mCurrentTarget->GetTransform();
4623 IntPoint quantizeScale = QuantizeScale(aFont, currentTransform);
4624 Matrix quantizeTransform = currentTransform;
4625 quantizeTransform.PostScale(quantizeScale.x, quantizeScale.y);
4626 HashNumber hash =
4627 GlyphCacheEntry::HashGlyphs(aBuffer, quantizeTransform, quantizeScale);
4628 DeviceColor colorOrMask =
4629 useBitmaps ? color
4630 : (usePreblend ? QuantizePreblendColor(color, aUseSubpixelAA)
4631 : DeviceColor::Mask(aUseSubpixelAA ? 1 : 0, 1));
4632 IntRect clipRect(IntPoint(), mViewportSize);
4633 RefPtr<GlyphCacheEntry> entry =
4634 cache->FindEntry(aBuffer, colorOrMask, quantizeTransform, quantizeScale,
4635 clipRect, hash, aStrokeOptions);
4636 if (!entry) {
4637 // For small text runs, bounds computations can be expensive relative to the
4638 // cost of looking up a cache result. Avoid doing local bounds computations
4639 // until actually inserting the entry into the cache.
4640 Maybe<Rect> bounds = mCurrentTarget->mSkia->GetGlyphLocalBounds(
4641 aFont, aBuffer, aPattern, aStrokeOptions, aOptions);
4642 if (!bounds) {
4643 // Assume the buffer is full of whitespace characters that should be
4644 // remembered for subsequent lookups.
4645 cache->SetLastWhitespace(aBuffer);
4646 return true;
4647 }
4648 // Transform the local bounds into device space so that we know how big
4649 // the cached texture will be.
4650 Rect xformBounds = currentTransform.TransformBounds(*bounds);
4651 // Check if the transform flattens out the bounds before rounding.
4652 if (xformBounds.IsEmpty()) {
4653 return true;
4654 }
4655 IntRect fullBounds = RoundedOut(xformBounds);
4656 IntRect clipBounds = fullBounds.Intersect(clipRect);
4657 // Check if the bounds are completely clipped out.
4658 if (clipBounds.IsEmpty()) {
4659 return true;
4660 }
4661 entry = cache->InsertEntry(aBuffer, colorOrMask, quantizeTransform,
4662 quantizeScale, clipBounds, fullBounds, hash,
4663 aStrokeOptions);
4664 if (!entry) {
4665 return false;
4666 }
4667 }
4668
4669 // The bounds of the entry may have a different transform offset from the
4670 // bounds of the currently drawn text run. The entry bounds are relative to
4671 // the entry's quantized offset already, so just move the bounds to the new
4672 // offset.
4673 IntRect intBounds = entry->GetBounds();
4674 IntPoint newOffset =
4675 QuantizeOffset(quantizeTransform, quantizeScale, aBuffer);
4676 intBounds +=
4677 IntPoint(newOffset.x / quantizeScale.x, newOffset.y / quantizeScale.y);
4678 // Ensure there is a clear border around the text. This must be applied only
4679 // after clipping so that we always have some border texels for filtering.
4680 intBounds.Inflate(2);
4681
4682 RefPtr<TextureHandle> handle = entry->GetHandle();
4683 if (handle && handle->IsValid()) {
4684 // If there is an entry with a valid cached texture handle, then try
4685 // to draw with that. If that for some reason failed, then fall back
4686 // to using the Skia target as that means we were preventing from
4687 // drawing to the WebGL context based on something other than the
4688 // texture.
4689 SurfacePattern pattern(nullptr, ExtendMode::CLAMP,
4690 Matrix::Translation(intBounds.TopLeft()));
4691 if (DrawRectAccel(Rect(intBounds), pattern, aOptions,
4692 useBitmaps ? Nothing() : Some(color), &handle, false,
4693 true, true)) {
4694 return true;
4695 }
4696 } else {
4697 handle = nullptr;
4698
4699 // If we get here, either there wasn't a cached texture handle or it
4700 // wasn't valid. Render the text run into a temporary target.
4701 RefPtr<DrawTargetSkia> textDT = new DrawTargetSkia;
4702 if (textDT->Init(intBounds.Size(),
4703 useBitmaps || usePreblend || aUseSubpixelAA
4704 ? SurfaceFormat::B8G8R8A8
4705 : SurfaceFormat::A8)) {
4706 textDT->SetTransform(currentTransform *
4707 Matrix::Translation(-intBounds.TopLeft()));
4708 textDT->SetPermitSubpixelAA(aUseSubpixelAA);
4709 DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER,
4710 aOptions.mAntialiasMode);
4711 // If bitmaps might be used, then we have to supply the color, as color
4712 // emoji may ignore it while grayscale bitmaps may use it, with no way to
4713 // know ahead of time. If we are using preblending in some form, then the
4714 // output also will depend on the supplied color. Otherwise, assume the
4715 // output will be a mask and just render it white to determine intensity.
4716 if (!useBitmaps && usePreblend) {
4717 textDT->DrawGlyphMask(aFont, aBuffer, color, aStrokeOptions,
4718 drawOptions);
4719 } else {
4720 ColorPattern colorPattern(useBitmaps ? color : DeviceColor(1, 1, 1, 1));
4721 if (aStrokeOptions) {
4722 textDT->StrokeGlyphs(aFont, aBuffer, colorPattern, *aStrokeOptions,
4723 drawOptions);
4724 } else {
4725 textDT->FillGlyphs(aFont, aBuffer, colorPattern, drawOptions);
4726 }
4727 }
4728 RefPtr<SourceSurface> textSurface = textDT->Snapshot();
4729 if (textSurface) {
4730 // If we don't expect the text surface to contain color glyphs
4731 // such as from subpixel AA, then do one final check to see if
4732 // any ended up in the result. If not, extract the alpha values
4733 // from the surface so we can render it as a mask.
4734 if (textSurface->GetFormat() != SurfaceFormat::A8 &&
4735 !CheckForColorGlyphs(textSurface)) {
4736 textSurface = ExtractAlpha(textSurface, !useBitmaps);
4737 if (!textSurface) {
4738 // Failed extracting alpha for the text surface...
4739 return false;
4740 }
4741 }
4742 // Attempt to upload the rendered text surface into a texture
4743 // handle and draw it.
4744 SurfacePattern pattern(textSurface, ExtendMode::CLAMP,
4745 Matrix::Translation(intBounds.TopLeft()));
4746 if (DrawRectAccel(Rect(intBounds), pattern, aOptions,
4747 useBitmaps ? Nothing() : Some(color), &handle, false,
4748 true) &&
4749 handle) {
4750 // If drawing succeeded, then the text surface was uploaded to
4751 // a texture handle. Assign it to the glyph cache entry.
4752 entry->Link(handle);
4753 } else {
4754 // If drawing failed, remove the entry from the cache.
4755 entry->Unlink();
4756 }
4757 return true;
4758 }
4759 }
4760 }
4761
4762 // Avoid filling cache with empty entries.
4763 entry->Unlink();
4764 return false;
4765}
4766
4767void DrawTargetWebgl::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
4768 const Pattern& aPattern,
4769 const DrawOptions& aOptions) {
4770 if (!aFont || !aBuffer.mNumGlyphs) {
4771 return;
4772 }
4773
4774 bool useSubpixelAA = ShouldUseSubpixelAA(aFont, aOptions);
4775
4776 if (mWebglValid && SupportsDrawOptions(aOptions) &&
4777 aPattern.GetType() == PatternType::COLOR && PrepareContext() &&
4778 mSharedContext->DrawGlyphsAccel(aFont, aBuffer, aPattern, aOptions,
4779 nullptr, useSubpixelAA)) {
4780 return;
4781 }
4782
4783 // If not able to cache the text run to a texture, then just fall back to
4784 // drawing with the Skia target.
4785 if (useSubpixelAA) {
4786 // Subpixel AA does not support layering because the subpixel masks can't
4787 // blend with the over op.
4788 MarkSkiaChanged();
4789 } else {
4790 MarkSkiaChanged(aOptions);
4791 }
4792 mSkia->FillGlyphs(aFont, aBuffer, aPattern, aOptions);
4793}
4794
4795// Attempts to read the contents of the WebGL context into the Skia target.
4796bool DrawTargetWebgl::ReadIntoSkia() {
4797 if (mSkiaValid) {
4798 return false;
4799 }
4800 bool didReadback = false;
4801 if (mWebglValid) {
4802 uint8_t* data = nullptr;
4803 IntSize size;
4804 int32_t stride;
4805 SurfaceFormat format;
4806 if (mIsClear) {
4807 // If the WebGL target is still clear, then just clear the Skia target.
4808 mSkia->DetachAllSnapshots();
4809 mSkiaNoClip->FillRect(Rect(mSkiaNoClip->GetRect()), GetClearPattern(),
4810 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
4811 } else {
4812 // If there's no existing snapshot and we can successfully map the Skia
4813 // target for reading, then try to read into that.
4814 if (!mSnapshot && mSkia->LockBits(&data, &size, &stride, &format)) {
4815 (void)ReadInto(data, stride);
4816 mSkia->ReleaseBits(data);
4817 } else if (RefPtr<SourceSurface> snapshot = Snapshot()) {
4818 // Otherwise, fall back to getting a snapshot from WebGL if available
4819 // and then copying that to Skia.
4820 mSkia->CopySurface(snapshot, GetRect(), IntPoint(0, 0));
4821 }
4822 didReadback = true;
4823 }
4824 }
4825 mSkiaValid = true;
4826 // The Skia data is flat after reading, so disable any layering.
4827 mSkiaLayer = false;
4828 return didReadback;
4829}
4830
4831// Reads data from the WebGL context and blends it with the current Skia layer.
4832void DrawTargetWebgl::FlattenSkia() {
4833 if (!mSkiaValid || !mSkiaLayer) {
4834 return;
4835 }
4836 mSkiaLayer = false;
4837 if (mSkiaLayerClear) {
4838 // If the WebGL target is clear, then there is nothing to blend.
4839 return;
4840 }
4841 if (RefPtr<DataSourceSurface> base = ReadSnapshot()) {
4842 mSkia->DetachAllSnapshots();
4843 mSkiaNoClip->DrawSurface(base, Rect(GetRect()), Rect(GetRect()),
4844 DrawSurfaceOptions(SamplingFilter::POINT),
4845 DrawOptions(1.f, CompositionOp::OP_DEST_OVER));
4846 }
4847}
4848
4849// Attempts to draw the contents of the Skia target into the WebGL context.
4850bool DrawTargetWebgl::FlushFromSkia() {
4851 // If the WebGL context has been lost, then mark it as invalid and fail.
4852 if (mSharedContext->IsContextLost()) {
4853 mWebglValid = false;
4854 return false;
4855 }
4856 // The WebGL target is already valid, so there is nothing to do.
4857 if (mWebglValid) {
4858 return true;
4859 }
4860 // Ensure that DrawRect doesn't recursively call into FlushFromSkia. If
4861 // the Skia target isn't valid, then it doesn't matter what is in the the
4862 // WebGL target either, so only try to blend if there is a valid Skia target.
4863 mWebglValid = true;
4864 if (mSkiaValid) {
4865 AutoRestoreContext restore(this);
4866
4867 // If the Skia target is clear, then there is no need to use a snapshot.
4868 // Directly clear the WebGL target instead.
4869 if (mIsClear) {
4870 if (!DrawRect(Rect(GetRect()), GetClearPattern(),
4871 DrawOptions(1.0f, CompositionOp::OP_SOURCE), Nothing(),
4872 nullptr, false, false, true)) {
4873 mWebglValid = false;
4874 return false;
4875 }
4876 return true;
4877 }
4878
4879 RefPtr<SourceSurface> skiaSnapshot = mSkia->Snapshot();
4880 if (!skiaSnapshot) {
4881 // There's a valid Skia target to draw to, but for some reason there is
4882 // no available snapshot, so just keep using the Skia target.
4883 mWebglValid = false;
4884 return false;
4885 }
4886
4887 // If there is no layer, then just upload it directly.
4888 if (!mSkiaLayer) {
4889 if (PrepareContext(false) && MarkChanged()) {
4890 if (RefPtr<DataSourceSurface> data = skiaSnapshot->GetDataSurface()) {
4891 mSharedContext->UploadSurface(data, mFormat, GetRect(), IntPoint(),
4892 false, false, mTex);
4893 return true;
4894 }
4895 }
4896 // Failed to upload the Skia snapshot.
4897 mWebglValid = false;
4898 return false;
4899 }
4900
4901 SurfacePattern pattern(skiaSnapshot, ExtendMode::CLAMP);
4902 // If there is a layer, blend the snapshot with the WebGL context.
4903 if (!DrawRect(Rect(GetRect()), pattern,
4904 DrawOptions(1.0f, CompositionOp::OP_OVER), Nothing(),
4905 &mSnapshotTexture, false, false, true, true)) {
4906 // If accelerated drawing failed for some reason, then leave the Skia
4907 // target unchanged.
4908 mWebglValid = false;
4909 return false;
4910 }
4911 }
4912 return true;
4913}
4914
4915void DrawTargetWebgl::UsageProfile::BeginFrame() {
4916 // Reset the usage profile counters for the new frame.
4917 mFallbacks = 0;
4918 mLayers = 0;
4919 mCacheMisses = 0;
4920 mCacheHits = 0;
4921 mUncachedDraws = 0;
4922 mReadbacks = 0;
4923}
4924
4925void DrawTargetWebgl::UsageProfile::EndFrame() {
4926 bool failed = false;
4927 // If we hit a complete fallback to software rendering, or if cache misses
4928 // were more than cutoff ratio of all requests, then we consider the frame as
4929 // having failed performance profiling.
4930 float cacheRatio =
4931 StaticPrefs::gfx_canvas_accelerated_profile_cache_miss_ratio();
4932 if (mFallbacks > 0 ||
4933 float(mCacheMisses + mReadbacks + mLayers) >
4934 cacheRatio * float(mCacheMisses + mCacheHits + mUncachedDraws +
4935 mReadbacks + mLayers)) {
4936 failed = true;
4937 }
4938 if (failed) {
4939 ++mFailedFrames;
4940 }
4941 ++mFrameCount;
4942}
4943
4944bool DrawTargetWebgl::UsageProfile::RequiresRefresh() const {
4945 // If we've rendered at least the required number of frames for a profile and
4946 // more than the cutoff ratio of frames did not meet performance criteria,
4947 // then we should stop using an accelerated canvas.
4948 uint32_t profileFrames = StaticPrefs::gfx_canvas_accelerated_profile_frames();
4949 if (!profileFrames || mFrameCount < profileFrames) {
4950 return false;
4951 }
4952 float failRatio =
4953 StaticPrefs::gfx_canvas_accelerated_profile_fallback_ratio();
4954 return mFailedFrames > failRatio * mFrameCount;
4955}
4956
4957void SharedContextWebgl::CachePrefs() {
4958 uint32_t capacity = StaticPrefs::gfx_canvas_accelerated_gpu_path_size() << 20;
4959 if (capacity != mPathVertexCapacity) {
4960 mPathVertexCapacity = capacity;
4961 if (mPathCache) {
4962 mPathCache->ClearVertexRanges();
4963 }
4964 if (mPathVertexBuffer) {
4965 ResetPathVertexBuffer();
4966 }
4967 }
4968
4969 mPathMaxComplexity =
4970 StaticPrefs::gfx_canvas_accelerated_gpu_path_complexity();
4971
4972 mPathAAStroke = StaticPrefs::gfx_canvas_accelerated_aa_stroke_enabled();
4973 mPathWGRStroke = StaticPrefs::gfx_canvas_accelerated_stroke_to_fill_path();
4974}
4975
4976// For use within CanvasRenderingContext2D, called on BorrowDrawTarget.
4977void DrawTargetWebgl::BeginFrame(bool aInvalidContents) {
4978 // If still rendering into the Skia target, switch back to the WebGL
4979 // context.
4980 if (!mWebglValid) {
4981 if (aInvalidContents) {
4982 // If nothing needs to persist, just mark the WebGL context valid.
4983 mWebglValid = true;
4984 // Even if the Skia framebuffer is marked clear, since the WebGL
4985 // context is not valid, its contents may be out-of-date and not
4986 // necessarily clear.
4987 mIsClear = false;
4988 } else {
4989 FlushFromSkia();
4990 }
4991 }
4992 // Check if we need to clear out any cached because of memory pressure.
4993 mSharedContext->ClearCachesIfNecessary();
4994 // Cache any prefs for the frame.
4995 mSharedContext->CachePrefs();
4996 mProfile.BeginFrame();
4997}
4998
4999// For use within CanvasRenderingContext2D, called on ReturnDrawTarget.
5000void DrawTargetWebgl::EndFrame() {
5001 if (StaticPrefs::gfx_canvas_accelerated_debug()) {
5002 // Draw a green rectangle in the upper right corner to indicate
5003 // acceleration.
5004 IntRect corner = IntRect(mSize.width - 16, 0, 16, 16).Intersect(GetRect());
5005 DrawRect(Rect(corner), ColorPattern(DeviceColor(0.0f, 1.0f, 0.0f, 1.0f)),
5006 DrawOptions(), Nothing(), nullptr, false, false);
5007 }
5008 mProfile.EndFrame();
5009 // Ensure we're not somehow using more than the allowed texture memory.
5010 mSharedContext->PruneTextureMemory();
5011 // Signal that we're done rendering the frame in case no present occurs.
5012 mSharedContext->mWebgl->EndOfFrame();
5013 // Check if we need to clear out any cached because of memory pressure.
5014 mSharedContext->ClearCachesIfNecessary();
5015}
5016
5017bool DrawTargetWebgl::CopyToSwapChain(
5018 layers::TextureType aTextureType, layers::RemoteTextureId aId,
5019 layers::RemoteTextureOwnerId aOwnerId,
5020 layers::RemoteTextureOwnerClient* aOwnerClient) {
5021 if (!mWebglValid && !FlushFromSkia()) {
5022 return false;
5023 }
5024
5025 // Copy and swizzle the WebGL framebuffer to the swap chain front buffer.
5026 webgl::SwapChainOptions options;
5027 options.bgra = true;
5028 // Allow async present to be toggled on for accelerated Canvas2D
5029 // independent of WebGL via pref.
5030 options.forceAsyncPresent =
5031 StaticPrefs::gfx_canvas_accelerated_async_present();
5032 options.remoteTextureId = aId;
5033 options.remoteTextureOwnerId = aOwnerId;
5034 return mSharedContext->mWebgl->CopyToSwapChain(mFramebuffer, aTextureType,
5035 options, aOwnerClient);
5036}
5037
5038already_AddRefed<DrawTarget> DrawTargetWebgl::CreateSimilarDrawTarget(
5039 const IntSize& aSize, SurfaceFormat aFormat) const {
5040 return mSkia->CreateSimilarDrawTarget(aSize, aFormat);
5041}
5042
5043bool DrawTargetWebgl::CanCreateSimilarDrawTarget(const IntSize& aSize,
5044 SurfaceFormat aFormat) const {
5045 return mSkia->CanCreateSimilarDrawTarget(aSize, aFormat);
5046}
5047
5048RefPtr<DrawTarget> DrawTargetWebgl::CreateClippedDrawTarget(
5049 const Rect& aBounds, SurfaceFormat aFormat) {
5050 return mSkia->CreateClippedDrawTarget(aBounds, aFormat);
5051}
5052
5053already_AddRefed<SourceSurface> DrawTargetWebgl::CreateSourceSurfaceFromData(
5054 unsigned char* aData, const IntSize& aSize, int32_t aStride,
5055 SurfaceFormat aFormat) const {
5056 return mSkia->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
5057}
5058
5059already_AddRefed<SourceSurface>
5060DrawTargetWebgl::CreateSourceSurfaceFromNativeSurface(
5061 const NativeSurface& aSurface) const {
5062 return mSkia->CreateSourceSurfaceFromNativeSurface(aSurface);
5063}
5064
5065already_AddRefed<SourceSurface> DrawTargetWebgl::OptimizeSourceSurface(
5066 SourceSurface* aSurface) const {
5067 if (aSurface->GetUnderlyingType() == SurfaceType::WEBGL) {
5068 return do_AddRef(aSurface);
5069 }
5070 return mSkia->OptimizeSourceSurface(aSurface);
5071}
5072
5073already_AddRefed<SourceSurface>
5074DrawTargetWebgl::OptimizeSourceSurfaceForUnknownAlpha(
5075 SourceSurface* aSurface) const {
5076 return mSkia->OptimizeSourceSurfaceForUnknownAlpha(aSurface);
5077}
5078
5079already_AddRefed<GradientStops> DrawTargetWebgl::CreateGradientStops(
5080 GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
5081 return mSkia->CreateGradientStops(aStops, aNumStops, aExtendMode);
5082}
5083
5084already_AddRefed<FilterNode> DrawTargetWebgl::CreateFilter(FilterType aType) {
5085 return mSkia->CreateFilter(aType);
5086}
5087
5088void DrawTargetWebgl::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
5089 const Point& aDestPoint,
5090 const DrawOptions& aOptions) {
5091 MarkSkiaChanged(aOptions);
5092 mSkia->DrawFilter(aNode, aSourceRect, aDestPoint, aOptions);
5093}
5094
5095bool DrawTargetWebgl::Draw3DTransformedSurface(SourceSurface* aSurface,
5096 const Matrix4x4& aMatrix) {
5097 MarkSkiaChanged();
5098 return mSkia->Draw3DTransformedSurface(aSurface, aMatrix);
5099}
5100
5101void DrawTargetWebgl::PushLayer(bool aOpaque, Float aOpacity,
5102 SourceSurface* aMask,
5103 const Matrix& aMaskTransform,
5104 const IntRect& aBounds, bool aCopyBackground) {
5105 PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
5106 aCopyBackground, CompositionOp::OP_OVER);
5107}
5108
5109void DrawTargetWebgl::PushLayerWithBlend(bool aOpaque, Float aOpacity,
5110 SourceSurface* aMask,
5111 const Matrix& aMaskTransform,
5112 const IntRect& aBounds,
5113 bool aCopyBackground,
5114 CompositionOp aCompositionOp) {
5115 MarkSkiaChanged(DrawOptions(aOpacity, aCompositionOp));
5116 mSkia->PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
5117 aCopyBackground, aCompositionOp);
5118 ++mLayerDepth;
5119 SetPermitSubpixelAA(mSkia->GetPermitSubpixelAA());
5120}
5121
5122void DrawTargetWebgl::PopLayer() {
5123 MOZ_ASSERT(mSkiaValid)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mSkiaValid)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mSkiaValid))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("mSkiaValid", "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp"
, 5123); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mSkiaValid"
")"); do { MOZ_CrashSequence(__null, 5123); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5124 MOZ_ASSERT(mLayerDepth > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mLayerDepth > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mLayerDepth > 0))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mLayerDepth > 0"
, "/root/firefox-clang/dom/canvas/DrawTargetWebgl.cpp", 5124)
; AnnotateMozCrashReason("MOZ_ASSERT" "(" "mLayerDepth > 0"
")"); do { MOZ_CrashSequence(__null, 5124); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5125 --mLayerDepth;
5126 mSkia->PopLayer();
5127 SetPermitSubpixelAA(mSkia->GetPermitSubpixelAA());
5128}
5129
5130} // namespace mozilla::gfx

/root/firefox-clang/dom/canvas/DrawTargetWebglInternal.h

1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7#ifndef _MOZILLA_GFX_DRAWTARGETWEBGL_INTERNAL_H
8#define _MOZILLA_GFX_DRAWTARGETWEBGL_INTERNAL_H
9
10#include "DrawTargetWebgl.h"
11
12#include "mozilla/HashFunctions.h"
13#include "mozilla/gfx/Etagere.h"
14#include "mozilla/gfx/PathSkia.h"
15#include "mozilla/gfx/WPFGpuRaster.h"
16
17namespace mozilla::gfx {
18
19// CacheEnty is a generic interface for various items that need to be cached to
20// a texture.
21class CacheEntry : public RefCounted<CacheEntry> {
22 public:
23 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CacheEntry)virtual const char* typeName() const { return "CacheEntry"; }
virtual size_t typeSize() const { return sizeof(*this); }
24
25 CacheEntry(const Matrix& aTransform, const IntRect& aBounds, HashNumber aHash)
26 : mTransform(aTransform), mBounds(aBounds), mHash(aHash) {}
27 virtual ~CacheEntry() = default;
28
29 void Link(const RefPtr<TextureHandle>& aHandle);
30 void Unlink();
31
32 const RefPtr<TextureHandle>& GetHandle() const { return mHandle; }
33
34 const Matrix& GetTransform() const { return mTransform; }
35 const IntRect& GetBounds() const { return mBounds; }
36 HashNumber GetHash() const { return mHash; }
37
38 virtual bool IsValid() const { return true; }
39
40 protected:
41 virtual void RemoveFromList() = 0;
42
43 // The handle of the rendered cache item.
44 RefPtr<TextureHandle> mHandle;
45 // The transform that was used to render the entry. This is necessary as
46 // the geometry might only be correctly rendered in device space after
47 // the transform is applied, so in general we can't cache untransformed
48 // geometry.
49 Matrix mTransform;
50 // The device space bounds of the rendered geometry.
51 IntRect mBounds;
52 // A hash of the geometry that may be used for quickly rejecting entries.
53 HashNumber mHash;
54};
55
56// CacheEntryImpl provides type-dependent boilerplate code for implementations
57// of CacheEntry.
58template <typename T>
59class CacheEntryImpl : public CacheEntry, public LinkedListElement<RefPtr<T>> {
60 typedef LinkedListElement<RefPtr<T>> ListType;
61
62 public:
63 CacheEntryImpl(const Matrix& aTransform, const IntRect& aBounds,
64 HashNumber aHash)
65 : CacheEntry(aTransform, aBounds, aHash) {}
66
67 void RemoveFromList() override {
68 if (ListType::isInList()) {
10
Taking true branch
69 ListType::remove();
11
Calling 'LinkedListElement::remove'
26
Returning; memory was released
70 }
71 }
72};
73
74// CacheImpl manages a list of CacheEntry.
75template <typename T, bool BIG>
76class CacheImpl {
77 protected:
78 typedef LinkedList<RefPtr<T>> ListType;
79
80 // Whether the cache should be small and space-efficient or prioritize speed.
81 static constexpr size_t kNumChains = BIG ? 499 : 71;
82
83 public:
84 ~CacheImpl() {
85 for (auto& chain : mChains) {
86 while (RefPtr<T> entry = chain.popLast()) {
3
Calling 'RefPtr::operator bool'
5
Returning from 'RefPtr::operator bool'
6
Loop condition is true. Entering loop body
87 entry->Unlink();
7
Calling 'CacheEntry::Unlink'
28
Returning; memory was released
88 }
29
Calling '~RefPtr'
89 }
90 }
91
92 protected:
93 ListType& GetChain(HashNumber aHash) { return mChains[aHash % kNumChains]; }
94
95 void Insert(T* aEntry) { GetChain(aEntry->GetHash()).insertFront(aEntry); }
96
97 ListType mChains[kNumChains];
98};
99
100// BackingTexture provides information about the shared or standalone texture
101// that is backing a texture handle.
102class BackingTexture {
103 public:
104 BackingTexture(const IntSize& aSize, SurfaceFormat aFormat,
105 const RefPtr<WebGLTexture>& aTexture);
106
107 SurfaceFormat GetFormat() const { return mFormat; }
108 IntSize GetSize() const { return mSize; }
109
110 static inline size_t UsedBytes(SurfaceFormat aFormat, const IntSize& aSize) {
111 return size_t(BytesPerPixel(aFormat)) * size_t(aSize.width) *
112 size_t(aSize.height);
113 }
114
115 size_t UsedBytes() const { return UsedBytes(GetFormat(), GetSize()); }
116
117 const RefPtr<WebGLTexture>& GetWebGLTexture() const { return mTexture; }
118
119 bool IsInitialized() const { return mFlags & INITIALIZED; }
120 void MarkInitialized() { mFlags |= INITIALIZED; }
121
122 bool IsRenderable() const { return mFlags & RENDERABLE; }
123 void MarkRenderable() { mFlags |= RENDERABLE; }
124
125 protected:
126 IntSize mSize;
127 SurfaceFormat mFormat;
128 RefPtr<WebGLTexture> mTexture;
129
130 private:
131 enum Flags : uint8_t {
132 INITIALIZED = 1 << 0,
133 RENDERABLE = 1 << 1,
134 };
135
136 uint8_t mFlags = 0;
137};
138
139// TextureHandle is an abstract base class for supplying textures to drawing
140// commands that may be backed by different resource types (such as a shared
141// or standalone texture). It may be further linked to use-specific metadata
142// such as for shadow drawing or for cached entries in the glyph cache.
143class TextureHandle : public RefCounted<TextureHandle>,
144 public LinkedListElement<RefPtr<TextureHandle>> {
145 public:
146 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(TextureHandle)virtual const char* typeName() const { return "TextureHandle"
; } virtual size_t typeSize() const { return sizeof(*this); }
147
148 enum Type { SHARED, STANDALONE };
149
150 virtual Type GetType() const = 0;
151 virtual IntRect GetBounds() const = 0;
152 IntSize GetSize() const { return GetBounds().Size(); }
153 virtual SurfaceFormat GetFormat() const = 0;
154
155 virtual BackingTexture* GetBackingTexture() = 0;
156
157 size_t UsedBytes() const {
158 return BackingTexture::UsedBytes(GetFormat(), GetSize());
159 }
160
161 virtual void UpdateSize(const IntSize& aSize) {}
162
163 virtual void Cleanup(SharedContextWebgl& aContext) {}
164
165 virtual ~TextureHandle() {}
166
167 bool IsValid() const { return mValid; }
168 void Invalidate() { mValid = false; }
169
170 void ClearSurface() { mSurface = nullptr; }
171 void SetSurface(const RefPtr<SourceSurface>& aSurface) {
172 mSurface = aSurface;
173 }
174 already_AddRefed<SourceSurface> GetSurface() const {
175 RefPtr<SourceSurface> surface(mSurface);
176 return surface.forget();
177 }
178
179 float GetSigma() const { return mSigma; }
180 void SetSigma(float aSigma) { mSigma = aSigma; }
181 bool IsShadow() const { return mSigma >= 0.0f; }
182
183 void SetSamplingOffset(const IntPoint& aSamplingOffset) {
184 mSamplingOffset = aSamplingOffset;
185 }
186 const IntPoint& GetSamplingOffset() const { return mSamplingOffset; }
187 IntRect GetSamplingRect() const {
188 return IntRect(GetSamplingOffset(), GetSize());
189 }
190
191 const RefPtr<CacheEntry>& GetCacheEntry() const { return mCacheEntry; }
192 void SetCacheEntry(const RefPtr<CacheEntry>& aEntry) { mCacheEntry = aEntry; }
193
194 // Note as used if there is corresponding surface or cache entry.
195 bool IsUsed() const {
196 return !mSurface.IsDead() || (mCacheEntry && mCacheEntry->IsValid());
197 }
198
199 private:
200 bool mValid = true;
201 // If applicable, weak pointer to the SourceSurface that is linked to this
202 // TextureHandle.
203 ThreadSafeWeakPtr<SourceSurface> mSurface;
204 // If this TextureHandle stores a cached shadow, then we need to remember the
205 // blur sigma used to produce the shadow.
206 float mSigma = -1.0f;
207 // If the originating surface requested a sampling rect, then we need to know
208 // the offset of the subrect within the surface for texture coordinates.
209 IntPoint mSamplingOffset;
210 // If applicable, the CacheEntry that is linked to this TextureHandle.
211 RefPtr<CacheEntry> mCacheEntry;
212};
213
214class SharedTextureHandle;
215
216// SharedTexture is a large slab texture that is subdivided (by using a
217// TexturePacker) to hold many small SharedTextureHandles. This avoids needing
218// to allocate many WebGL textures for every single small Canvas 2D texture.
219class SharedTexture : public RefCounted<SharedTexture>, public BackingTexture {
220 public:
221 MOZ_DECLARE_REFCOUNTED_TYPENAME(SharedTexture)const char* typeName() const { return "SharedTexture"; } size_t
typeSize() const { return sizeof(*this); }
222
223 SharedTexture(const IntSize& aSize, SurfaceFormat aFormat,
224 const RefPtr<WebGLTexture>& aTexture);
225 ~SharedTexture();
226
227 already_AddRefed<SharedTextureHandle> Allocate(const IntSize& aSize);
228 bool Free(SharedTextureHandle& aHandle);
229
230 bool HasAllocatedHandles() const {
231 return mAtlasAllocator && Etagere::etagere_atlas_allocator_allocated_space(
232 mAtlasAllocator) > 0;
233 }
234
235 private:
236 Etagere::AtlasAllocator* mAtlasAllocator = nullptr;
237};
238
239// SharedTextureHandle is an allocated region within a large SharedTexture page
240// that owns it.
241class SharedTextureHandle : public TextureHandle {
242 friend class SharedTexture;
243
244 public:
245 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SharedTextureHandle, override)virtual const char* typeName() const override { return "SharedTextureHandle"
; } virtual size_t typeSize() const override { return sizeof(
*this); }
246
247 SharedTextureHandle(Etagere::AllocationId aId, const IntRect& aBounds,
248 SharedTexture* aTexture);
249
250 Type GetType() const override { return Type::SHARED; }
251
252 IntRect GetBounds() const override { return mBounds; }
253
254 SurfaceFormat GetFormat() const override { return mTexture->GetFormat(); }
255
256 BackingTexture* GetBackingTexture() override { return mTexture.get(); }
257
258 void Cleanup(SharedContextWebgl& aContext) override;
259
260 const RefPtr<SharedTexture>& GetOwner() const { return mTexture; }
261
262 private:
263 Etagere::AllocationId mAllocationId = Etagere::INVALID_ALLOCATION_ID;
264 IntRect mBounds;
265 RefPtr<SharedTexture> mTexture;
266};
267
268// StandaloneTexture is a texture that can not be effectively shared within
269// a SharedTexture page, such that it is better to assign it its own WebGL
270// texture.
271class StandaloneTexture : public TextureHandle, public BackingTexture {
272 public:
273 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(StandaloneTexture, override)virtual const char* typeName() const override { return "StandaloneTexture"
; } virtual size_t typeSize() const override { return sizeof(
*this); }
274
275 StandaloneTexture(const IntSize& aSize, SurfaceFormat aFormat,
276 const RefPtr<WebGLTexture>& aTexture);
277
278 Type GetType() const override { return Type::STANDALONE; }
279
280 IntRect GetBounds() const override {
281 return IntRect(IntPoint(0, 0), BackingTexture::GetSize());
282 }
283
284 SurfaceFormat GetFormat() const override {
285 return BackingTexture::GetFormat();
286 }
287
288 using BackingTexture::UsedBytes;
289
290 BackingTexture* GetBackingTexture() override { return this; }
291
292 void UpdateSize(const IntSize& aSize) override { mSize = aSize; }
293
294 void Cleanup(SharedContextWebgl& aContext) override;
295};
296
297// GlyphCacheEntry stores rendering metadata for a rendered text run, as well
298// the handle to the texture it was rendered into, so that it can be located
299// for reuse under similar rendering circumstances.
300class GlyphCacheEntry : public CacheEntryImpl<GlyphCacheEntry> {
301 public:
302 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphCacheEntry, override)virtual const char* typeName() const override { return "GlyphCacheEntry"
; } virtual size_t typeSize() const override { return sizeof(
*this); }
303
304 GlyphCacheEntry(const GlyphBuffer& aBuffer, const DeviceColor& aColor,
305 const Matrix& aTransform, const IntPoint& aQuantizeScale,
306 const IntRect& aBounds, const IntRect& aFullBounds,
307 HashNumber aHash,
308 StoredStrokeOptions* aStrokeOptions = nullptr);
309 ~GlyphCacheEntry();
310
311 const GlyphBuffer& GetGlyphBuffer() const { return mBuffer; }
312
313 bool MatchesGlyphs(const GlyphBuffer& aBuffer, const DeviceColor& aColor,
314 const Matrix& aTransform, const IntPoint& aQuantizeOffset,
315 const IntPoint& aBoundsOffset, const IntRect& aClipRect,
316 HashNumber aHash, const StrokeOptions* aStrokeOptions);
317
318 static HashNumber HashGlyphs(const GlyphBuffer& aBuffer,
319 const Matrix& aTransform,
320 const IntPoint& aQuantizeScale);
321
322 private:
323 // The glyph keys used to render the text run.
324 GlyphBuffer mBuffer = {nullptr, 0};
325 // The color of the text run.
326 DeviceColor mColor;
327 // The full bounds of the text run without any clipping applied.
328 IntRect mFullBounds;
329 // Stroke options for the text run.
330 UniquePtr<StoredStrokeOptions> mStrokeOptions;
331};
332
333// GlyphCache maintains a list of GlyphCacheEntry's representing previously
334// rendered text runs. The cache is searched to see if a given incoming text
335// run has already been rendered to a texture, and if so, just reuses it.
336// Otherwise, the text run will be rendered to a new texture handle and
337// inserted into a new GlyphCacheEntry to represent it.
338class GlyphCache : public LinkedListElement<GlyphCache>,
339 public CacheImpl<GlyphCacheEntry, false> {
340 public:
341 explicit GlyphCache(ScaledFont* aFont);
342
343 ScaledFont* GetFont() const { return mFont; }
344
345 already_AddRefed<GlyphCacheEntry> FindEntry(const GlyphBuffer& aBuffer,
346 const DeviceColor& aColor,
347 const Matrix& aTransform,
348 const IntPoint& aQuantizeScale,
349 const IntRect& aClipRect,
350 HashNumber aHash,
351 const StrokeOptions* aOptions);
352
353 already_AddRefed<GlyphCacheEntry> InsertEntry(
354 const GlyphBuffer& aBuffer, const DeviceColor& aColor,
355 const Matrix& aTransform, const IntPoint& aQuantizeScale,
356 const IntRect& aBounds, const IntRect& aFullBounds, HashNumber aHash,
357 const StrokeOptions* aOptions);
358
359 bool IsWhitespace(const GlyphBuffer& aBuffer) const;
360 void SetLastWhitespace(const GlyphBuffer& aBuffer);
361
362 private:
363 // Weak pointer to the owning font
364 ScaledFont* mFont;
365 // The last whitespace queried from this cache
366 Maybe<uint32_t> mLastWhitespace;
367};
368
369struct QuantizedPath {
370 explicit QuantizedPath(const WGR::Path& aPath);
371 // Ensure the path can only be moved, but not copied.
372 QuantizedPath(QuantizedPath&&) noexcept;
373 QuantizedPath(const QuantizedPath&) = delete;
374 ~QuantizedPath();
375
376 bool operator==(const QuantizedPath&) const;
377
378 WGR::Path mPath;
379};
380
381struct PathVertexRange {
382 uint32_t mOffset;
383 uint32_t mLength;
384
385 PathVertexRange() : mOffset(0), mLength(0) {}
386 PathVertexRange(uint32_t aOffset, uint32_t aLength)
387 : mOffset(aOffset), mLength(aLength) {}
388
389 bool IsValid() const { return mLength > 0; }
390};
391
392enum class AAStrokeMode {
393 Unsupported,
394 Geometry,
395 Mask,
396};
397
398// PathCacheEntry stores a rasterized version of a supplied path with a given
399// pattern.
400class PathCacheEntry : public CacheEntryImpl<PathCacheEntry> {
401 public:
402 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathCacheEntry, override)virtual const char* typeName() const override { return "PathCacheEntry"
; } virtual size_t typeSize() const override { return sizeof(
*this); }
403
404 PathCacheEntry(QuantizedPath&& aPath, Pattern* aPattern,
405 StoredStrokeOptions* aStrokeOptions, AAStrokeMode aStrokeMode,
406 const Matrix& aTransform, const IntRect& aBounds,
407 const Point& aOrigin, HashNumber aHash, float aSigma = -1.0f);
408
409 bool MatchesPath(const QuantizedPath& aPath, const Pattern* aPattern,
410 const StrokeOptions* aStrokeOptions,
411 AAStrokeMode aStrokeMode, const Matrix& aTransform,
412 const IntRect& aBounds, const Point& aOrigin,
413 HashNumber aHash, float aSigma);
414
415 static HashNumber HashPath(const QuantizedPath& aPath,
416 const Pattern* aPattern, const Matrix& aTransform,
417 const IntRect& aBounds, const Point& aOrigin);
418
419 const QuantizedPath& GetPath() const { return mPath; }
420
421 const Point& GetOrigin() const { return mOrigin; }
422
423 // Valid if either a mask (no pattern) or there is valid pattern.
424 bool IsValid() const override { return !mPattern || mPattern->IsValid(); }
425
426 const PathVertexRange& GetVertexRange() const { return mVertexRange; }
427 void SetVertexRange(const PathVertexRange& aRange) { mVertexRange = aRange; }
428
429 private:
430 // The actual path geometry supplied
431 QuantizedPath mPath;
432 // The transformed origin of the path
433 Point mOrigin;
434 // The pattern used to rasterize the path, if not a mask
435 UniquePtr<Pattern> mPattern;
436 // The StrokeOptions used for stroked paths, if applicable
437 UniquePtr<StoredStrokeOptions> mStrokeOptions;
438 // The AAStroke mode used for rendering a stroked path.
439 AAStrokeMode mAAStrokeMode = AAStrokeMode::Unsupported;
440 // The shadow blur sigma
441 float mSigma;
442 // If the path has cached geometry in the vertex buffer.
443 PathVertexRange mVertexRange;
444};
445
446class PathCache : public CacheImpl<PathCacheEntry, true> {
447 public:
448 PathCache() = default;
449
450 already_AddRefed<PathCacheEntry> FindOrInsertEntry(
451 QuantizedPath aPath, const Pattern* aPattern,
452 const StrokeOptions* aStrokeOptions, AAStrokeMode aStrokeMode,
453 const Matrix& aTransform, const IntRect& aBounds, const Point& aOrigin,
454 float aSigma = -1.0f);
455
456 void ClearVertexRanges();
457};
458
459} // namespace mozilla::gfx
460
461#endif // _MOZILLA_GFX_DRAWTARGETWEBGL_INTERNAL_H

/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h

1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7#ifndef mozilla_RefPtr_h
8#define mozilla_RefPtr_h
9
10#include "mozilla/AlreadyAddRefed.h"
11#include "mozilla/Assertions.h"
12#include "mozilla/Attributes.h"
13#include "mozilla/DbgMacro.h"
14
15#include <type_traits>
16
17/*****************************************************************************/
18
19// template <class T> class RefPtrGetterAddRefs;
20
21class nsQueryReferent;
22class nsCOMPtr_helper;
23class nsISupports;
24
25namespace mozilla {
26template <class T>
27class MovingNotNull;
28template <class T>
29class NotNull;
30template <class T>
31class OwningNonNull;
32template <class T>
33class StaticLocalRefPtr;
34template <class T>
35class StaticRefPtr;
36
37// Traditionally, RefPtr supports automatic refcounting of any pointer type
38// with AddRef() and Release() methods that follow the traditional semantics.
39//
40// This traits class can be specialized to operate on other pointer types. For
41// example, we specialize this trait for opaque FFI types that represent
42// refcounted objects in Rust.
43//
44// Given the use of ConstRemovingRefPtrTraits below, U should not be a const-
45// qualified type.
46template <class U>
47struct RefPtrTraits {
48 static void AddRef(U* aPtr) { aPtr->AddRef(); }
49 static void Release(U* aPtr) { aPtr->Release(); }
50};
51
52} // namespace mozilla
53
54template <class T>
55class MOZ_IS_REFPTR RefPtr {
56 private:
57 void assign_with_AddRef(T* aRawPtr) {
58 if (aRawPtr) {
59 ConstRemovingRefPtrTraits<T>::AddRef(aRawPtr);
60 }
61 assign_assuming_AddRef(aRawPtr);
62 }
63
64 void assign_assuming_AddRef(T* aNewPtr) {
65 T* oldPtr = mRawPtr;
66 mRawPtr = aNewPtr;
67 if (oldPtr) {
68 ConstRemovingRefPtrTraits<T>::Release(oldPtr);
69 }
70 }
71
72 private:
73 T* MOZ_OWNING_REF mRawPtr;
74
75 public:
76 typedef T element_type;
77
78 ~RefPtr() {
79 if (mRawPtr
29.1
Field 'mRawPtr' is non-null
29.1
Field 'mRawPtr' is non-null
29.1
Field 'mRawPtr' is non-null
29.1
Field 'mRawPtr' is non-null
29.1
Field 'mRawPtr' is non-null
29.1
Field 'mRawPtr' is non-null
) {
30
Taking true branch
80 ConstRemovingRefPtrTraits<T>::Release(mRawPtr);
31
Use of memory after it is freed
81 }
82 }
83
84 // Constructors
85
86 constexpr RefPtr()
87 : mRawPtr(nullptr)
88 // default constructor
89 {}
90
91 RefPtr(const RefPtr<T>& aSmartPtr)
92 : mRawPtr(aSmartPtr.mRawPtr)
93 // copy-constructor
94 {
95 if (mRawPtr) {
96 ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
97 }
98 }
99
100 RefPtr(RefPtr<T>&& aRefPtr) noexcept : mRawPtr(aRefPtr.mRawPtr) {
101 aRefPtr.mRawPtr = nullptr;
102 }
103
104 // construct from a raw pointer (of the right type)
105
106 MOZ_IMPLICIT RefPtr(T* aRawPtr) : mRawPtr(aRawPtr) {
107 if (mRawPtr) {
108 ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
109 }
110 }
111
112 MOZ_IMPLICIT RefPtr(decltype(nullptr)) : mRawPtr(nullptr) {}
113
114 template <typename I,
115 typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
116 MOZ_IMPLICIT RefPtr(already_AddRefed<I>& aSmartPtr)
117 : mRawPtr(aSmartPtr.take())
118 // construct from |already_AddRefed|
119 {}
120
121 template <typename I,
122 typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
123 MOZ_IMPLICIT RefPtr(already_AddRefed<I>&& aSmartPtr)
124 : mRawPtr(aSmartPtr.take())
125 // construct from |otherRefPtr.forget()|
126 {}
127
128 template <typename I,
129 typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
130 MOZ_IMPLICIT RefPtr(const RefPtr<I>& aSmartPtr)
131 : mRawPtr(aSmartPtr.get())
132 // copy-construct from a smart pointer with a related pointer type
133 {
134 if (mRawPtr) {
135 ConstRemovingRefPtrTraits<T>::AddRef(mRawPtr);
136 }
137 }
138
139 template <typename I,
140 typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
141 MOZ_IMPLICIT RefPtr(RefPtr<I>&& aSmartPtr)
142 : mRawPtr(aSmartPtr.forget().take())
143 // construct from |Move(RefPtr<SomeSubclassOfT>)|.
144 {}
145
146 template <typename I,
147 typename = std::enable_if_t<!std::is_same_v<I, RefPtr<T>> &&
148 std::is_convertible_v<I, RefPtr<T>>>>
149 MOZ_IMPLICIT RefPtr(const mozilla::NotNull<I>& aSmartPtr)
150 : mRawPtr(RefPtr<T>(aSmartPtr.get()).forget().take())
151 // construct from |mozilla::NotNull|.
152 {}
153
154 template <typename I,
155 typename = std::enable_if_t<!std::is_same_v<I, RefPtr<T>> &&
156 std::is_convertible_v<I, RefPtr<T>>>>
157 MOZ_IMPLICIT RefPtr(mozilla::MovingNotNull<I>&& aSmartPtr)
158 : mRawPtr(RefPtr<T>(std::move(aSmartPtr).unwrapBasePtr()).forget().take())
159 // construct from |mozilla::MovingNotNull|.
160 {}
161
162 MOZ_IMPLICIT RefPtr(const nsQueryReferent& aHelper);
163 MOZ_IMPLICIT RefPtr(const nsCOMPtr_helper& aHelper);
164
165 // Defined in OwningNonNull.h
166 template <class U>
167 MOZ_IMPLICIT RefPtr(const mozilla::OwningNonNull<U>& aOther);
168
169 // Defined in StaticLocalPtr.h
170 template <class U>
171 MOZ_IMPLICIT RefPtr(const mozilla::StaticLocalRefPtr<U>& aOther);
172
173 // Defined in StaticPtr.h
174 template <class U>
175 MOZ_IMPLICIT RefPtr(const mozilla::StaticRefPtr<U>& aOther);
176
177 // Assignment operators
178
179 RefPtr<T>& operator=(decltype(nullptr)) {
180 assign_assuming_AddRef(nullptr);
181 return *this;
182 }
183
184 RefPtr<T>& operator=(const RefPtr<T>& aRhs)
185 // copy assignment operator
186 {
187 assign_with_AddRef(aRhs.mRawPtr);
188 return *this;
189 }
190
191 template <typename I>
192 RefPtr<T>& operator=(const RefPtr<I>& aRhs)
193 // assign from an RefPtr of a related pointer type
194 {
195 assign_with_AddRef(aRhs.get());
196 return *this;
197 }
198
199 RefPtr<T>& operator=(T* aRhs)
200 // assign from a raw pointer (of the right type)
201 {
202 assign_with_AddRef(aRhs);
203 return *this;
204 }
205
206 template <typename I>
207 RefPtr<T>& operator=(already_AddRefed<I>& aRhs)
208 // assign from |already_AddRefed|
209 {
210 assign_assuming_AddRef(aRhs.take());
211 return *this;
212 }
213
214 template <typename I>
215 RefPtr<T>& operator=(already_AddRefed<I>&& aRhs)
216 // assign from |otherRefPtr.forget()|
217 {
218 assign_assuming_AddRef(aRhs.take());
219 return *this;
220 }
221
222 RefPtr<T>& operator=(const nsQueryReferent& aQueryReferent);
223 RefPtr<T>& operator=(const nsCOMPtr_helper& aHelper);
224
225 template <typename I,
226 typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
227 RefPtr<T>& operator=(RefPtr<I>&& aRefPtr) noexcept {
228 assign_assuming_AddRef(aRefPtr.forget().take());
229 return *this;
230 }
231
232 template <typename I,
233 typename = std::enable_if_t<std::is_convertible_v<I, RefPtr<T>>>>
234 RefPtr<T>& operator=(const mozilla::NotNull<I>& aSmartPtr)
235 // assign from |mozilla::NotNull|.
236 {
237 assign_assuming_AddRef(RefPtr<T>(aSmartPtr.get()).forget().take());
238 return *this;
239 }
240
241 template <typename I,
242 typename = std::enable_if_t<std::is_convertible_v<I, RefPtr<T>>>>
243 RefPtr<T>& operator=(mozilla::MovingNotNull<I>&& aSmartPtr)
244 // assign from |mozilla::MovingNotNull|.
245 {
246 assign_assuming_AddRef(
247 RefPtr<T>(std::move(aSmartPtr).unwrapBasePtr()).forget().take());
248 return *this;
249 }
250
251 // Defined in OwningNonNull.h
252 template <class U>
253 RefPtr<T>& operator=(const mozilla::OwningNonNull<U>& aOther);
254
255 // Defined in StaticLocalPtr.h
256 template <class U>
257 RefPtr<T>& operator=(const mozilla::StaticLocalRefPtr<U>& aOther);
258
259 // Defined in StaticPtr.h
260 template <class U>
261 RefPtr<T>& operator=(const mozilla::StaticRefPtr<U>& aOther);
262
263 // Other pointer operators
264
265 void swap(RefPtr<T>& aRhs)
266 // ...exchange ownership with |aRhs|; can save a pair of refcount operations
267 {
268 T* temp = aRhs.mRawPtr;
269 aRhs.mRawPtr = mRawPtr;
270 mRawPtr = temp;
271 }
272
273 void swap(T*& aRhs)
274 // ...exchange ownership with |aRhs|; can save a pair of refcount operations
275 {
276 T* temp = aRhs;
277 aRhs = mRawPtr;
278 mRawPtr = temp;
279 }
280
281 already_AddRefed<T> MOZ_MAY_CALL_AFTER_MUST_RETURN forget()
282 // return the value of mRawPtr and null out mRawPtr. Useful for
283 // already_AddRefed return values.
284 {
285 T* temp = nullptr;
286 swap(temp);
287 return already_AddRefed<T>(temp);
288 }
289
290 template <typename I>
291 void forget(I** aRhs)
292 // Set the target of aRhs to the value of mRawPtr and null out mRawPtr.
293 // Useful to avoid unnecessary AddRef/Release pairs with "out"
294 // parameters where aRhs bay be a T** or an I** where I is a base class
295 // of T.
296 {
297 MOZ_ASSERT(aRhs, "Null pointer passed to forget!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aRhs)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(aRhs))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aRhs" " (" "Null pointer passed to forget!"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 297); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aRhs" ") (" "Null pointer passed to forget!"
")"); do { MOZ_CrashSequence(__null, 297); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
298 *aRhs = mRawPtr;
299 mRawPtr = nullptr;
300 }
301
302 void forget(nsISupports** aRhs) {
303 MOZ_ASSERT(aRhs, "Null pointer passed to forget!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aRhs)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(aRhs))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aRhs" " (" "Null pointer passed to forget!"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 303); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aRhs" ") (" "Null pointer passed to forget!"
")"); do { MOZ_CrashSequence(__null, 303); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
304 *aRhs = ToSupports(mRawPtr);
305 mRawPtr = nullptr;
306 }
307
308 T* get() const
309 /*
310 Prefer the implicit conversion provided automatically by |operator T*()
311 const|. Use |get()| to resolve ambiguity or to get a castable pointer.
312 */
313 {
314 return const_cast<T*>(mRawPtr);
315 }
316
317 operator T*() const&
318 /*
319 ...makes an |RefPtr| act like its underlying raw pointer type whenever it
320 is used in a context where a raw pointer is expected. It is this operator
321 that makes an |RefPtr| substitutable for a raw pointer.
322
323 Prefer the implicit use of this operator to calling |get()|, except where
324 necessary to resolve ambiguity.
325 */
326 {
327 return get();
328 }
329
330 // Don't allow implicit conversion of temporary RefPtr to raw pointer,
331 // because the refcount might be one and the pointer will immediately become
332 // invalid.
333 operator T*() const&& = delete;
334
335 // These are needed to avoid the deleted operator above. XXX Why is operator!
336 // needed separately? Shouldn't the compiler prefer using the non-deleted
337 // operator bool instead of the deleted operator T*?
338 explicit operator bool() const { return !!mRawPtr; }
4
Assuming field 'mRawPtr' is non-null
339 bool operator!() const { return !mRawPtr; }
340
341 T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
342 MOZ_ASSERT(mRawPtr != nullptr,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRawPtr != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRawPtr != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mRawPtr != nullptr"
" (" "You can't dereference a NULL RefPtr with operator->()."
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 343); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRawPtr != nullptr"
") (" "You can't dereference a NULL RefPtr with operator->()."
")"); do { MOZ_CrashSequence(__null, 343); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
343 "You can't dereference a NULL RefPtr with operator->().")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRawPtr != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRawPtr != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mRawPtr != nullptr"
" (" "You can't dereference a NULL RefPtr with operator->()."
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 343); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRawPtr != nullptr"
") (" "You can't dereference a NULL RefPtr with operator->()."
")"); do { MOZ_CrashSequence(__null, 343); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
344 return get();
345 }
346
347 template <typename R, typename... Args>
348 class Proxy {
349 typedef R (T::*member_function)(Args...);
350 T* mRawPtr;
351 member_function mFunction;
352
353 public:
354 Proxy(T* aRawPtr, member_function aFunction)
355 : mRawPtr(aRawPtr), mFunction(aFunction) {}
356 template <typename... ActualArgs>
357 R operator()(ActualArgs&&... aArgs) {
358 return ((*mRawPtr).*mFunction)(std::forward<ActualArgs>(aArgs)...);
359 }
360 };
361
362 template <typename R, typename... Args>
363 Proxy<R, Args...> operator->*(R (T::*aFptr)(Args...)) const {
364 MOZ_ASSERT(mRawPtr != nullptr,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRawPtr != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRawPtr != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mRawPtr != nullptr"
" (" "You can't dereference a NULL RefPtr with operator->*()."
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 365); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRawPtr != nullptr"
") (" "You can't dereference a NULL RefPtr with operator->*()."
")"); do { MOZ_CrashSequence(__null, 365); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
365 "You can't dereference a NULL RefPtr with operator->*().")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRawPtr != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRawPtr != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mRawPtr != nullptr"
" (" "You can't dereference a NULL RefPtr with operator->*()."
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 365); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRawPtr != nullptr"
") (" "You can't dereference a NULL RefPtr with operator->*()."
")"); do { MOZ_CrashSequence(__null, 365); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
366 return Proxy<R, Args...>(get(), aFptr);
367 }
368
369 RefPtr<T>* get_address()
370 // This is not intended to be used by clients. See |address_of|
371 // below.
372 {
373 return this;
374 }
375
376 const RefPtr<T>* get_address() const
377 // This is not intended to be used by clients. See |address_of|
378 // below.
379 {
380 return this;
381 }
382
383 public:
384 T& operator*() const {
385 MOZ_ASSERT(mRawPtr != nullptr,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRawPtr != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRawPtr != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mRawPtr != nullptr"
" (" "You can't dereference a NULL RefPtr with operator*()."
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRawPtr != nullptr"
") (" "You can't dereference a NULL RefPtr with operator*()."
")"); do { MOZ_CrashSequence(__null, 386); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
386 "You can't dereference a NULL RefPtr with operator*().")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRawPtr != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRawPtr != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mRawPtr != nullptr"
" (" "You can't dereference a NULL RefPtr with operator*()."
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefPtr.h"
, 386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRawPtr != nullptr"
") (" "You can't dereference a NULL RefPtr with operator*()."
")"); do { MOZ_CrashSequence(__null, 386); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
387 return *get();
388 }
389
390 T** StartAssignment() {
391 assign_assuming_AddRef(nullptr);
392 return reinterpret_cast<T**>(&mRawPtr);
393 }
394
395 private:
396 // This helper class makes |RefPtr<const T>| possible by casting away
397 // the constness from the pointer when calling AddRef() and Release().
398 //
399 // This is necessary because AddRef() and Release() implementations can't
400 // generally expected to be const themselves (without heavy use of |mutable|
401 // and |const_cast| in their own implementations).
402 //
403 // This should be sound because while |RefPtr<const T>| provides a
404 // const view of an object, the object itself should not be const (it
405 // would have to be allocated as |new const T| or similar to be const).
406 template <class U>
407 struct ConstRemovingRefPtrTraits {
408 static void AddRef(U* aPtr) { mozilla::RefPtrTraits<U>::AddRef(aPtr); }
409 static void Release(U* aPtr) { mozilla::RefPtrTraits<U>::Release(aPtr); }
410 };
411 template <class U>
412 struct ConstRemovingRefPtrTraits<const U> {
413 static void AddRef(const U* aPtr) {
414 mozilla::RefPtrTraits<U>::AddRef(const_cast<U*>(aPtr));
415 }
416 static void Release(const U* aPtr) {
417 mozilla::RefPtrTraits<U>::Release(const_cast<U*>(aPtr));
418 }
419 };
420};
421
422class nsCycleCollectionTraversalCallback;
423template <typename T>
424void CycleCollectionNoteChild(nsCycleCollectionTraversalCallback& aCallback,
425 T* aChild, const char* aName, uint32_t aFlags);
426
427template <typename T>
428inline void ImplCycleCollectionUnlink(RefPtr<T>& aField) {
429 aField = nullptr;
430}
431
432template <typename T>
433inline void ImplCycleCollectionTraverse(
434 nsCycleCollectionTraversalCallback& aCallback, const RefPtr<T>& aField,
435 const char* aName, uint32_t aFlags = 0) {
436 CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
437}
438
439template <class T>
440inline RefPtr<T>* address_of(RefPtr<T>& aPtr) {
441 return aPtr.get_address();
442}
443
444template <class T>
445inline const RefPtr<T>* address_of(const RefPtr<T>& aPtr) {
446 return aPtr.get_address();
447}
448
449template <class T>
450class RefPtrGetterAddRefs
451/*
452 ...
453
454 This class is designed to be used for anonymous temporary objects in the
455 argument list of calls that return COM interface pointers, e.g.,
456
457 RefPtr<IFoo> fooP;
458 ...->GetAddRefedPointer(getter_AddRefs(fooP))
459
460 DO NOT USE THIS TYPE DIRECTLY IN YOUR CODE. Use |getter_AddRefs()| instead.
461
462 When initialized with a |RefPtr|, as in the example above, it returns
463 a |void**|, a |T**|, or an |nsISupports**| as needed, that the
464 outer call (|GetAddRefedPointer| in this case) can fill in.
465
466 This type should be a nested class inside |RefPtr<T>|.
467*/
468{
469 public:
470 explicit RefPtrGetterAddRefs(RefPtr<T>& aSmartPtr)
471 : mTargetSmartPtr(aSmartPtr) {
472 // nothing else to do
473 }
474
475 operator void**() {
476 return reinterpret_cast<void**>(mTargetSmartPtr.StartAssignment());
477 }
478
479 operator T**() { return mTargetSmartPtr.StartAssignment(); }
480
481 T*& operator*() { return *(mTargetSmartPtr.StartAssignment()); }
482
483 private:
484 RefPtr<T>& mTargetSmartPtr;
485};
486
487template <class T>
488inline RefPtrGetterAddRefs<T> getter_AddRefs(RefPtr<T>& aSmartPtr)
489/*
490 Used around a |RefPtr| when
491 ...makes the class |RefPtrGetterAddRefs<T>| invisible.
492*/
493{
494 return RefPtrGetterAddRefs<T>(aSmartPtr);
495}
496
497// Comparing two |RefPtr|s
498
499template <class T, class U>
500inline bool operator==(const RefPtr<T>& aLhs, const RefPtr<U>& aRhs) {
501 return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs.get());
502}
503
504template <class T, class U>
505inline bool operator!=(const RefPtr<T>& aLhs, const RefPtr<U>& aRhs) {
506 return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs.get());
507}
508
509// Comparing an |RefPtr| to a raw pointer
510
511template <class T, class U>
512inline bool operator==(const RefPtr<T>& aLhs, const U* aRhs) {
513 return static_cast<const T*>(aLhs.get()) == static_cast<const U*>(aRhs);
514}
515
516template <class T, class U>
517inline bool operator==(const U* aLhs, const RefPtr<T>& aRhs) {
518 return static_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
519}
520
521template <class T, class U>
522inline bool operator!=(const RefPtr<T>& aLhs, const U* aRhs) {
523 return static_cast<const T*>(aLhs.get()) != static_cast<const U*>(aRhs);
524}
525
526template <class T, class U>
527inline bool operator!=(const U* aLhs, const RefPtr<T>& aRhs) {
528 return static_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
529}
530
531template <class T, class U>
532inline bool operator==(const RefPtr<T>& aLhs, U* aRhs) {
533 return static_cast<const T*>(aLhs.get()) == const_cast<const U*>(aRhs);
534}
535
536template <class T, class U>
537inline bool operator==(U* aLhs, const RefPtr<T>& aRhs) {
538 return const_cast<const U*>(aLhs) == static_cast<const T*>(aRhs.get());
539}
540
541template <class T, class U>
542inline bool operator!=(const RefPtr<T>& aLhs, U* aRhs) {
543 return static_cast<const T*>(aLhs.get()) != const_cast<const U*>(aRhs);
544}
545
546template <class T, class U>
547inline bool operator!=(U* aLhs, const RefPtr<T>& aRhs) {
548 return const_cast<const U*>(aLhs) != static_cast<const T*>(aRhs.get());
549}
550
551// Comparing an |RefPtr| to |nullptr|
552
553template <class T>
554inline bool operator==(const RefPtr<T>& aLhs, decltype(nullptr)) {
555 return aLhs.get() == nullptr;
556}
557
558template <class T>
559inline bool operator==(decltype(nullptr), const RefPtr<T>& aRhs) {
560 return nullptr == aRhs.get();
561}
562
563template <class T>
564inline bool operator!=(const RefPtr<T>& aLhs, decltype(nullptr)) {
565 return aLhs.get() != nullptr;
566}
567
568template <class T>
569inline bool operator!=(decltype(nullptr), const RefPtr<T>& aRhs) {
570 return nullptr != aRhs.get();
571}
572
573// MOZ_DBG support
574
575template <class T>
576std::ostream& operator<<(std::ostream& aOut, const RefPtr<T>& aObj) {
577 return mozilla::DebugValue(aOut, aObj.get());
578}
579
580/*****************************************************************************/
581
582template <class T>
583inline already_AddRefed<T> do_AddRef(T* aObj) {
584 RefPtr<T> ref(aObj);
585 return ref.forget();
586}
587
588template <class T>
589inline already_AddRefed<T> do_AddRef(const RefPtr<T>& aObj) {
590 RefPtr<T> ref(aObj);
591 return ref.forget();
592}
593
594namespace mozilla {
595
596template <typename T>
597class AlignmentFinder;
598
599// Provide a specialization of AlignmentFinder to allow MOZ_ALIGNOF(RefPtr<T>)
600// with an incomplete T.
601template <typename T>
602class AlignmentFinder<RefPtr<T>> {
603 public:
604 static const size_t alignment = alignof(T*);
605};
606
607/**
608 * Helper function to be able to conveniently write things like:
609 *
610 * already_AddRefed<T>
611 * f(...)
612 * {
613 * return MakeAndAddRef<T>(...);
614 * }
615 */
616template <typename T, typename... Args>
617already_AddRefed<T> MakeAndAddRef(Args&&... aArgs) {
618 RefPtr<T> p(new T(std::forward<Args>(aArgs)...));
619 return p.forget();
620}
621
622/**
623 * Helper function to be able to conveniently write things like:
624 *
625 * auto runnable =
626 * MakeRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>>(
627 * mOnSuccess, mOnFailure, *error, mWindowID);
628 */
629template <typename T, typename... Args>
630RefPtr<T> MakeRefPtr(Args&&... aArgs) {
631 RefPtr<T> p(new T(std::forward<Args>(aArgs)...));
632 return p;
633}
634
635} // namespace mozilla
636
637/**
638 * Deduction guide to allow simple `RefPtr` definitions from an
639 * already_AddRefed<T> without repeating the type, e.g.:
640 *
641 * RefPtr ptr = MakeAndAddRef<SomeType>(...);
642 */
643template <typename T>
644RefPtr(already_AddRefed<T>) -> RefPtr<T>;
645
646#endif /* mozilla_RefPtr_h */

/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h

1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7/* A type-safe doubly-linked list class. */
8
9/*
10 * The classes LinkedList<T> and LinkedListElement<T> together form a
11 * convenient, type-safe doubly-linked list implementation.
12 *
13 * The class T which will be inserted into the linked list must inherit from
14 * LinkedListElement<T>. A given object may be in only one linked list at a
15 * time.
16 *
17 * A LinkedListElement automatically removes itself from the list upon
18 * destruction, and a LinkedList will fatally assert in debug builds if it's
19 * non-empty when it's destructed.
20 *
21 * For example, you might use LinkedList in a simple observer list class as
22 * follows.
23 *
24 * class Observer : public LinkedListElement<Observer>
25 * {
26 * public:
27 * void observe(char* aTopic) { ... }
28 * };
29 *
30 * class ObserverContainer
31 * {
32 * private:
33 * LinkedList<Observer> list;
34 *
35 * public:
36 * void addObserver(Observer* aObserver)
37 * {
38 * // Will assert if |aObserver| is part of another list.
39 * list.insertBack(aObserver);
40 * }
41 *
42 * void removeObserver(Observer* aObserver)
43 * {
44 * // Will assert if |aObserver| is not part of some list.
45 * aObserver.remove();
46 * // Or, will assert if |aObserver| is not part of |list| specifically.
47 * // aObserver.removeFrom(list);
48 * }
49 *
50 * void notifyObservers(char* aTopic)
51 * {
52 * for (Observer* o = list.getFirst(); o != nullptr; o = o->getNext()) {
53 * o->observe(aTopic);
54 * }
55 * }
56 * };
57 *
58 * Additionally, the class AutoCleanLinkedList<T> is a LinkedList<T> that will
59 * remove and delete each element still within itself upon destruction. Note
60 * that because each element is deleted, elements must have been allocated
61 * using |new|.
62 */
63
64#ifndef mozilla_LinkedList_h
65#define mozilla_LinkedList_h
66
67#include <algorithm>
68#include <utility>
69
70#include "mozilla/Assertions.h"
71#include "mozilla/Attributes.h"
72#include "mozilla/MemoryReporting.h"
73#include "mozilla/RefPtr.h"
74
75#ifdef __cplusplus201703L
76
77namespace mozilla {
78
79template <typename T>
80class LinkedListElement;
81
82namespace detail {
83
84/**
85 * LinkedList supports refcounted elements using this adapter class. Clients
86 * using LinkedList<RefPtr<T>> will get a data structure that holds a strong
87 * reference to T as long as T is in the list.
88 */
89template <typename T>
90struct LinkedListElementTraits {
91 typedef T* RawType;
92 typedef const T* ConstRawType;
93 typedef T* ClientType;
94 typedef const T* ConstClientType;
95
96 // These static methods are called when an element is added to or removed from
97 // a linked list. It can be used to keep track ownership in lists that are
98 // supposed to own their elements. If elements are transferred from one list
99 // to another, no enter or exit calls happen since the elements still belong
100 // to a list.
101 static void enterList(LinkedListElement<T>* elt) {}
102 static void exitList(LinkedListElement<T>* elt) {}
103
104 // This method is called when AutoCleanLinkedList cleans itself
105 // during destruction. It can be used to call delete on elements if
106 // the list is the sole owner.
107 static void cleanElement(LinkedListElement<T>* elt) { delete elt->asT(); }
108};
109
110template <typename T>
111struct LinkedListElementTraits<RefPtr<T>> {
112 typedef T* RawType;
113 typedef const T* ConstRawType;
114 typedef RefPtr<T> ClientType;
115 typedef RefPtr<const T> ConstClientType;
116
117 static void enterList(LinkedListElement<RefPtr<T>>* elt) {
118 elt->asT()->AddRef();
119 }
120 static void exitList(LinkedListElement<RefPtr<T>>* elt) {
121 elt->asT()->Release();
15
Calling 'RefCounted::Release'
24
Returning; memory was released
122 }
123 static void cleanElement(LinkedListElement<RefPtr<T>>* elt) {}
124};
125
126} /* namespace detail */
127
128template <typename T>
129class LinkedList;
130
131template <typename T>
132class LinkedListElement {
133 typedef typename detail::LinkedListElementTraits<T> Traits;
134 typedef typename Traits::RawType RawType;
135 typedef typename Traits::ConstRawType ConstRawType;
136 typedef typename Traits::ClientType ClientType;
137 typedef typename Traits::ConstClientType ConstClientType;
138
139 /*
140 * It's convenient that we return nullptr when getNext() or getPrevious()
141 * hits the end of the list, but doing so costs an extra word of storage in
142 * each linked list node (to keep track of whether |this| is the sentinel
143 * node) and a branch on this value in getNext/getPrevious.
144 *
145 * We could get rid of the extra word of storage by shoving the "is
146 * sentinel" bit into one of the pointers, although this would, of course,
147 * have performance implications of its own.
148 *
149 * But the goal here isn't to win an award for the fastest or slimmest
150 * linked list; rather, we want a *convenient* linked list. So we won't
151 * waste time guessing which micro-optimization strategy is best.
152 *
153 *
154 * Speaking of unnecessary work, it's worth addressing here why we wrote
155 * mozilla::LinkedList in the first place, instead of using stl::list.
156 *
157 * The key difference between mozilla::LinkedList and stl::list is that
158 * mozilla::LinkedList stores the mPrev/mNext pointers in the object itself,
159 * while stl::list stores the mPrev/mNext pointers in a list element which
160 * itself points to the object being stored.
161 *
162 * mozilla::LinkedList's approach makes it harder to store an object in more
163 * than one list. But the upside is that you can call next() / prev() /
164 * remove() directly on the object. With stl::list, you'd need to store a
165 * pointer to its iterator in the object in order to accomplish this. Not
166 * only would this waste space, but you'd have to remember to update that
167 * pointer every time you added or removed the object from a list.
168 *
169 * In-place, constant-time removal is a killer feature of doubly-linked
170 * lists, and supporting this painlessly was a key design criterion.
171 */
172
173 private:
174 LinkedListElement* mNext;
175 LinkedListElement* mPrev;
176 const bool mIsSentinel;
177
178 public:
179 LinkedListElement() : mNext(this), mPrev(this), mIsSentinel(false) {}
180
181 /*
182 * Moves |aOther| into |*this|. If |aOther| is already in a list, then
183 * |aOther| is removed from the list and replaced by |*this|.
184 */
185 LinkedListElement(LinkedListElement<T>&& aOther)
186 : mIsSentinel(aOther.mIsSentinel) {
187 adjustLinkForMove(std::move(aOther));
188 }
189
190 LinkedListElement& operator=(LinkedListElement<T>&& aOther) {
191 MOZ_ASSERT(mIsSentinel == aOther.mIsSentinel, "Mismatch NodeKind!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mIsSentinel == aOther.mIsSentinel)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mIsSentinel == aOther.mIsSentinel
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"mIsSentinel == aOther.mIsSentinel" " (" "Mismatch NodeKind!"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 191); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mIsSentinel == aOther.mIsSentinel"
") (" "Mismatch NodeKind!" ")"); do { MOZ_CrashSequence(__null
, 191); __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
;
192 MOZ_ASSERT(!isInList(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isInList())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isInList()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!isInList()" " ("
"Assigning to an element in a list messes up that list!" ")"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isInList()"
") (" "Assigning to an element in a list messes up that list!"
")"); do { MOZ_CrashSequence(__null, 193); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
193 "Assigning to an element in a list messes up that list!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isInList())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isInList()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!isInList()" " ("
"Assigning to an element in a list messes up that list!" ")"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isInList()"
") (" "Assigning to an element in a list messes up that list!"
")"); do { MOZ_CrashSequence(__null, 193); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
194 adjustLinkForMove(std::move(aOther));
195 return *this;
196 }
197
198 ~LinkedListElement() {
199 if (!mIsSentinel && isInList()) {
200 remove();
201 }
202 }
203
204 /*
205 * Get the next element in the list, or nullptr if this is the last element
206 * in the list.
207 */
208 RawType getNext() { return mNext->asT(); }
209 ConstRawType getNext() const { return mNext->asT(); }
210
211 /*
212 * Get the previous element in the list, or nullptr if this is the first
213 * element in the list.
214 */
215 RawType getPrevious() { return mPrev->asT(); }
216 ConstRawType getPrevious() const { return mPrev->asT(); }
217
218 /*
219 * Insert aElem after this element in the list. |this| must be part of a
220 * linked list when you call setNext(); otherwise, this method will assert.
221 */
222 void setNext(RawType aElem) {
223 MOZ_ASSERT(isInList())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isInList())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isInList()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("isInList()", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 223); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isInList()" ")"
); do { MOZ_CrashSequence(__null, 223); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
224 setNextUnsafe(aElem);
225 }
226
227 /*
228 * Insert aElem before this element in the list. |this| must be part of a
229 * linked list when you call setPrevious(); otherwise, this method will
230 * assert.
231 */
232 void setPrevious(RawType aElem) {
233 MOZ_ASSERT(isInList())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isInList())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isInList()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("isInList()", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 233); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isInList()" ")"
); do { MOZ_CrashSequence(__null, 233); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
234 setPreviousUnsafe(aElem);
235 }
236
237 /*
238 * Remove this element from the list which contains it. If this element is
239 * not currently part of a linked list, this method asserts.
240 */
241 void remove() {
242 MOZ_ASSERT(isInList())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isInList())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isInList()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("isInList()", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 242); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isInList()" ")"
); do { MOZ_CrashSequence(__null, 242); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
12
Taking false branch
13
Loop condition is false. Exiting loop
243
244 mPrev->mNext = mNext;
245 mNext->mPrev = mPrev;
246 mNext = this;
247 mPrev = this;
248
249 Traits::exitList(this);
14
Calling 'LinkedListElementTraits::exitList'
25
Returning; memory was released via 1st parameter
250 }
251
252 /*
253 * Remove this element from the list containing it. Returns a pointer to the
254 * element that follows this element (before it was removed). This method
255 * asserts if the element does not belong to a list. Note: In a refcounted
256 * list, |this| may be destroyed.
257 */
258 RawType removeAndGetNext() {
259 RawType r = getNext();
260 remove();
261 return r;
262 }
263
264 /*
265 * Remove this element from the list containing it. Returns a pointer to the
266 * previous element in the containing list (before the removal). This method
267 * asserts if the element does not belong to a list. Note: In a refcounted
268 * list, |this| may be destroyed.
269 */
270 RawType removeAndGetPrevious() {
271 RawType r = getPrevious();
272 remove();
273 return r;
274 }
275
276 /*
277 * Identical to remove(), but also asserts in debug builds that this element
278 * is in aList.
279 */
280 void removeFrom(const LinkedList<T>& aList) {
281 aList.assertContains(asT());
282 remove();
283 }
284
285 /*
286 * Return true if |this| part is of a linked list, and false otherwise.
287 */
288 bool isInList() const {
289 MOZ_ASSERT((mNext == this) == (mPrev == this))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mNext == this) == (mPrev == this))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((mNext == this) == (mPrev ==
this)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(mNext == this) == (mPrev == this)", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 289); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(mNext == this) == (mPrev == this)"
")"); do { MOZ_CrashSequence(__null, 289); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
290 return mNext != this;
291 }
292
293 private:
294 friend class LinkedList<T>;
295 friend struct detail::LinkedListElementTraits<T>;
296
297 enum class NodeKind { Normal, Sentinel };
298
299 explicit LinkedListElement(NodeKind nodeKind)
300 : mNext(this), mPrev(this), mIsSentinel(nodeKind == NodeKind::Sentinel) {}
301
302 /*
303 * Return |this| cast to T* if we're a normal node, or return nullptr if
304 * we're a sentinel node.
305 */
306 RawType asT() { return mIsSentinel ? nullptr : static_cast<RawType>(this); }
307 ConstRawType asT() const {
308 return mIsSentinel ? nullptr : static_cast<ConstRawType>(this);
309 }
310
311 /*
312 * Insert aElem after this element, but don't check that this element is in
313 * the list. This is called by LinkedList::insertFront().
314 */
315 void setNextUnsafe(RawType aElem) {
316 LinkedListElement* listElem = static_cast<LinkedListElement*>(aElem);
317 MOZ_RELEASE_ASSERT(!listElem->isInList())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!listElem->isInList())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!listElem->isInList()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!listElem->isInList()"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 317); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!listElem->isInList()"
")"); do { MOZ_CrashSequence(__null, 317); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
318
319 listElem->mNext = this->mNext;
320 listElem->mPrev = this;
321 this->mNext->mPrev = listElem;
322 this->mNext = listElem;
323
324 Traits::enterList(aElem);
325 }
326
327 /*
328 * Insert aElem before this element, but don't check that this element is in
329 * the list. This is called by LinkedList::insertBack().
330 */
331 void setPreviousUnsafe(RawType aElem) {
332 LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(aElem);
333 MOZ_RELEASE_ASSERT(!listElem->isInList())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!listElem->isInList())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!listElem->isInList()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!listElem->isInList()"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 333); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!listElem->isInList()"
")"); do { MOZ_CrashSequence(__null, 333); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
334
335 listElem->mNext = this;
336 listElem->mPrev = this->mPrev;
337 this->mPrev->mNext = listElem;
338 this->mPrev = listElem;
339
340 Traits::enterList(aElem);
341 }
342
343 /*
344 * Transfers the elements [aBegin, aEnd) before the "this" list element.
345 */
346 void transferBeforeUnsafe(LinkedListElement<T>& aBegin,
347 LinkedListElement<T>& aEnd) {
348 MOZ_RELEASE_ASSERT(!aBegin.mIsSentinel)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aBegin.mIsSentinel)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aBegin.mIsSentinel))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("!aBegin.mIsSentinel"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 348); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "!aBegin.mIsSentinel"
")"); do { MOZ_CrashSequence(__null, 348); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
349 if (!aBegin.isInList() || !aEnd.isInList()) {
350 return;
351 }
352
353 auto otherPrev = aBegin.mPrev;
354
355 aBegin.mPrev = this->mPrev;
356 this->mPrev->mNext = &aBegin;
357 this->mPrev = aEnd.mPrev;
358 aEnd.mPrev->mNext = this;
359
360 // Patch the gap in the source list
361 otherPrev->mNext = &aEnd;
362 aEnd.mPrev = otherPrev;
363 }
364
365 /*
366 * Adjust mNext and mPrev for implementing move constructor and move
367 * assignment.
368 */
369 void adjustLinkForMove(LinkedListElement<T>&& aOther) {
370 if (!aOther.isInList()) {
371 mNext = this;
372 mPrev = this;
373 return;
374 }
375
376 if (!mIsSentinel) {
377 Traits::enterList(this);
378 }
379
380 MOZ_ASSERT(aOther.mNext->mPrev == &aOther)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOther.mNext->mPrev == &aOther)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(aOther.mNext->mPrev == &aOther))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aOther.mNext->mPrev == &aOther"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 380); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOther.mNext->mPrev == &aOther"
")"); do { MOZ_CrashSequence(__null, 380); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
381 MOZ_ASSERT(aOther.mPrev->mNext == &aOther)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOther.mPrev->mNext == &aOther)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(aOther.mPrev->mNext == &aOther))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aOther.mPrev->mNext == &aOther"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 381); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOther.mPrev->mNext == &aOther"
")"); do { MOZ_CrashSequence(__null, 381); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
382
383 /*
384 * Initialize |this| with |aOther|'s mPrev/mNext pointers, and adjust those
385 * element to point to this one.
386 */
387 mNext = aOther.mNext;
388 mPrev = aOther.mPrev;
389
390 mNext->mPrev = this;
391 mPrev->mNext = this;
392
393 /*
394 * Adjust |aOther| so it doesn't think it's in a list. This makes it
395 * safely destructable.
396 */
397 aOther.mNext = &aOther;
398 aOther.mPrev = &aOther;
399
400 if (!mIsSentinel) {
401 Traits::exitList(&aOther);
402 }
403 }
404
405 LinkedListElement& operator=(const LinkedListElement<T>& aOther) = delete;
406 LinkedListElement(const LinkedListElement<T>& aOther) = delete;
407};
408
409template <typename T>
410class LinkedList {
411 private:
412 using Traits = typename detail::LinkedListElementTraits<T>;
413 using RawType = typename Traits::RawType;
414 using ConstRawType = typename Traits::ConstRawType;
415 using ClientType = typename Traits::ClientType;
416 using ConstClientType = typename Traits::ConstClientType;
417 using ElementType = LinkedListElement<T>*;
418 using ConstElementType = const LinkedListElement<T>*;
419
420 LinkedListElement<T> sentinel;
421
422 public:
423 template <typename Type, typename Element>
424 class Iterator {
425 Type mCurrent;
426
427 public:
428 using iterator_category = std::forward_iterator_tag;
429 using value_type = T;
430 using difference_type = std::ptrdiff_t;
431 using pointer = T*;
432 using reference = T&;
433
434 explicit Iterator(Type aCurrent) : mCurrent(aCurrent) {}
435
436 Type operator*() const { return mCurrent; }
437
438 const Iterator& operator++() {
439 mCurrent = static_cast<Element>(mCurrent)->getNext();
440 return *this;
441 }
442
443 bool operator!=(const Iterator& aOther) const {
444 return mCurrent != aOther.mCurrent;
445 }
446 };
447
448 using const_iterator = Iterator<ConstRawType, ConstElementType>;
449 using iterator = Iterator<RawType, ElementType>;
450
451 LinkedList() : sentinel(LinkedListElement<T>::NodeKind::Sentinel) {}
452
453 LinkedList(LinkedList<T>&& aOther) : sentinel(std::move(aOther.sentinel)) {}
454
455 LinkedList& operator=(LinkedList<T>&& aOther) {
456 MOZ_ASSERT(isEmpty(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isEmpty()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("isEmpty()" " (" "Assigning to a non-empty list leaks elements in that list!"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isEmpty()" ") ("
"Assigning to a non-empty list leaks elements in that list!"
")"); do { MOZ_CrashSequence(__null, 457); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
457 "Assigning to a non-empty list leaks elements in that list!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(isEmpty()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("isEmpty()" " (" "Assigning to a non-empty list leaks elements in that list!"
")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isEmpty()" ") ("
"Assigning to a non-empty list leaks elements in that list!"
")"); do { MOZ_CrashSequence(__null, 457); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
458 sentinel = std::move(aOther.sentinel);
459 return *this;
460 }
461
462 ~LinkedList() {
463# ifdef DEBUG1
464 if (!isEmpty()) {
465 MOZ_CRASH_UNSAFE_PRINTF(do { static_assert(1 > 0, "Did you forget arguments to MOZ_CRASH_UNSAFE_PRINTF? "
"Or maybe you want MOZ_CRASH instead?"); static_assert(1 <=
sPrintfMaxArgs, "Only up to 4 additional arguments are allowed!"
); static_assert(sizeof("%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction") <= sPrintfCrashReasonSize, "The supplied format string is too long!"
); MOZ_Crash("/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 469, MOZ_CrashPrintf("" "%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction", __PRETTY_FUNCTION__)); } while (false
)
466 "%s has a buggy user: "do { static_assert(1 > 0, "Did you forget arguments to MOZ_CRASH_UNSAFE_PRINTF? "
"Or maybe you want MOZ_CRASH instead?"); static_assert(1 <=
sPrintfMaxArgs, "Only up to 4 additional arguments are allowed!"
); static_assert(sizeof("%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction") <= sPrintfCrashReasonSize, "The supplied format string is too long!"
); MOZ_Crash("/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 469, MOZ_CrashPrintf("" "%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction", __PRETTY_FUNCTION__)); } while (false
)
467 "it should have removed all this list's elements before "do { static_assert(1 > 0, "Did you forget arguments to MOZ_CRASH_UNSAFE_PRINTF? "
"Or maybe you want MOZ_CRASH instead?"); static_assert(1 <=
sPrintfMaxArgs, "Only up to 4 additional arguments are allowed!"
); static_assert(sizeof("%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction") <= sPrintfCrashReasonSize, "The supplied format string is too long!"
); MOZ_Crash("/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 469, MOZ_CrashPrintf("" "%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction", __PRETTY_FUNCTION__)); } while (false
)
468 "the list's destruction",do { static_assert(1 > 0, "Did you forget arguments to MOZ_CRASH_UNSAFE_PRINTF? "
"Or maybe you want MOZ_CRASH instead?"); static_assert(1 <=
sPrintfMaxArgs, "Only up to 4 additional arguments are allowed!"
); static_assert(sizeof("%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction") <= sPrintfCrashReasonSize, "The supplied format string is too long!"
); MOZ_Crash("/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 469, MOZ_CrashPrintf("" "%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction", __PRETTY_FUNCTION__)); } while (false
)
469 __PRETTY_FUNCTION__)do { static_assert(1 > 0, "Did you forget arguments to MOZ_CRASH_UNSAFE_PRINTF? "
"Or maybe you want MOZ_CRASH instead?"); static_assert(1 <=
sPrintfMaxArgs, "Only up to 4 additional arguments are allowed!"
); static_assert(sizeof("%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction") <= sPrintfCrashReasonSize, "The supplied format string is too long!"
); MOZ_Crash("/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 469, MOZ_CrashPrintf("" "%s has a buggy user: " "it should have removed all this list's elements before "
"the list's destruction", __PRETTY_FUNCTION__)); } while (false
)
;
470 }
471# endif
472 }
473
474 /*
475 * Add aElem to the front of the list.
476 */
477 void insertFront(RawType aElem) {
478 /* Bypass setNext()'s this->isInList() assertion. */
479 sentinel.setNextUnsafe(aElem);
480 }
481
482 /*
483 * Add aElem to the back of the list.
484 */
485 void insertBack(RawType aElem) { sentinel.setPreviousUnsafe(aElem); }
486
487 /*
488 * Move all elements from another list to the back
489 */
490 void extendBack(LinkedList<T>&& aOther) {
491 MOZ_RELEASE_ASSERT(this != &aOther)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(this != &aOther)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(this != &aOther))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("this != &aOther"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 491); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "this != &aOther"
")"); do { MOZ_CrashSequence(__null, 491); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
492 if (aOther.isEmpty()) {
493 return;
494 }
495 sentinel.transferBeforeUnsafe(**aOther.begin(), aOther.sentinel);
496 }
497
498 /*
499 * Move elements from another list to the specified position
500 */
501 void splice(size_t aDestinationPos, LinkedList<T>& aListFrom,
502 size_t aSourceStart, size_t aSourceLen) {
503 MOZ_RELEASE_ASSERT(this != &aListFrom)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(this != &aListFrom)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(this != &aListFrom))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("this != &aListFrom"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 503); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "this != &aListFrom"
")"); do { MOZ_CrashSequence(__null, 503); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
504 if (aListFrom.isEmpty() || !aSourceLen) {
505 return;
506 }
507
508 const auto safeForward = [](LinkedList<T>& aList,
509 LinkedListElement<T>& aBegin,
510 size_t aPos) -> LinkedListElement<T>& {
511 auto* iter = &aBegin;
512 for (size_t i = 0; i < aPos; ++i, (iter = iter->mNext)) {
513 if (iter->mIsSentinel) {
514 break;
515 }
516 }
517 return *iter;
518 };
519
520 auto& sourceBegin =
521 safeForward(aListFrom, *aListFrom.sentinel.mNext, aSourceStart);
522 if (sourceBegin.mIsSentinel) {
523 return;
524 }
525 auto& sourceEnd = safeForward(aListFrom, sourceBegin, aSourceLen);
526 auto& destination = safeForward(*this, *sentinel.mNext, aDestinationPos);
527
528 destination.transferBeforeUnsafe(sourceBegin, sourceEnd);
529 }
530
531 /*
532 * Get the first element of the list, or nullptr if the list is empty.
533 */
534 RawType getFirst() { return sentinel.getNext(); }
535 ConstRawType getFirst() const { return sentinel.getNext(); }
536
537 /*
538 * Get the last element of the list, or nullptr if the list is empty.
539 */
540 RawType getLast() { return sentinel.getPrevious(); }
541 ConstRawType getLast() const { return sentinel.getPrevious(); }
542
543 /*
544 * Get and remove the first element of the list. If the list is empty,
545 * return nullptr.
546 */
547 ClientType popFirst() {
548 ClientType ret = sentinel.getNext();
549 if (ret) {
550 static_cast<LinkedListElement<T>*>(RawType(ret))->remove();
551 }
552 return ret;
553 }
554
555 /*
556 * Get and remove the last element of the list. If the list is empty,
557 * return nullptr.
558 */
559 ClientType popLast() {
560 ClientType ret = sentinel.getPrevious();
561 if (ret) {
562 static_cast<LinkedListElement<T>*>(RawType(ret))->remove();
563 }
564 return ret;
565 }
566
567 /*
568 * Return true if the list is empty, or false otherwise.
569 */
570 bool isEmpty() const { return !sentinel.isInList(); }
571
572 /**
573 * Returns whether the given element is in the list.
574 */
575 bool contains(ConstRawType aElm) const {
576 return std::find(begin(), end(), aElm) != end();
577 }
578
579 /*
580 * Remove all the elements from the list.
581 *
582 * This runs in time linear to the list's length, because we have to mark
583 * each element as not in the list.
584 */
585 void clear() {
586 while (popFirst()) {
587 }
588 }
589
590 /**
591 * Return the length of elements in the list.
592 */
593 size_t length() const { return std::distance(begin(), end()); }
594
595 /*
596 * Allow range-based iteration:
597 *
598 * for (MyElementType* elt : myList) { ... }
599 */
600 Iterator<RawType, ElementType> begin() {
601 return Iterator<RawType, ElementType>(getFirst());
602 }
603 Iterator<ConstRawType, ConstElementType> begin() const {
604 return Iterator<ConstRawType, ConstElementType>(getFirst());
605 }
606 Iterator<RawType, ElementType> end() {
607 return Iterator<RawType, ElementType>(nullptr);
608 }
609 Iterator<ConstRawType, ConstElementType> end() const {
610 return Iterator<ConstRawType, ConstElementType>(nullptr);
611 }
612
613 /*
614 * Measures the memory consumption of the list excluding |this|. Note that
615 * it only measures the list elements themselves. If the list elements
616 * contain pointers to other memory blocks, those blocks must be measured
617 * separately during a subsequent iteration over the list.
618 */
619 size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
620 size_t n = 0;
621 ConstRawType t = getFirst();
622 while (t) {
623 n += aMallocSizeOf(t);
624 t = static_cast<const LinkedListElement<T>*>(t)->getNext();
625 }
626 return n;
627 }
628
629 /*
630 * Like sizeOfExcludingThis(), but measures |this| as well.
631 */
632 size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
633 return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
634 }
635
636 /*
637 * In a debug build, make sure that the list is sane (no cycles, consistent
638 * mNext/mPrev pointers, only one sentinel). Has no effect in release builds.
639 */
640 void debugAssertIsSane() const {
641# ifdef DEBUG1
642 const LinkedListElement<T>* slow;
643 const LinkedListElement<T>* fast1;
644 const LinkedListElement<T>* fast2;
645
646 /*
647 * Check for cycles in the forward singly-linked list using the
648 * tortoise/hare algorithm.
649 */
650 for (slow = sentinel.mNext, fast1 = sentinel.mNext->mNext,
651 fast2 = sentinel.mNext->mNext->mNext;
652 slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
653 slow = slow->mNext, fast1 = fast2->mNext, fast2 = fast1->mNext) {
654 MOZ_ASSERT(slow != fast1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slow != fast1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slow != fast1))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("slow != fast1",
"/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 654); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slow != fast1"
")"); do { MOZ_CrashSequence(__null, 654); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
655 MOZ_ASSERT(slow != fast2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slow != fast2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slow != fast2))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("slow != fast2",
"/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slow != fast2"
")"); do { MOZ_CrashSequence(__null, 655); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
656 }
657
658 /* Check for cycles in the backward singly-linked list. */
659 for (slow = sentinel.mPrev, fast1 = sentinel.mPrev->mPrev,
660 fast2 = sentinel.mPrev->mPrev->mPrev;
661 slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
662 slow = slow->mPrev, fast1 = fast2->mPrev, fast2 = fast1->mPrev) {
663 MOZ_ASSERT(slow != fast1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slow != fast1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slow != fast1))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("slow != fast1",
"/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 663); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slow != fast1"
")"); do { MOZ_CrashSequence(__null, 663); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
664 MOZ_ASSERT(slow != fast2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slow != fast2)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slow != fast2))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("slow != fast2",
"/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 664); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slow != fast2"
")"); do { MOZ_CrashSequence(__null, 664); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
665 }
666
667 /*
668 * Check that |sentinel| is the only node in the list with
669 * mIsSentinel == true.
670 */
671 for (const LinkedListElement<T>* elem = sentinel.mNext; elem != &sentinel;
672 elem = elem->mNext) {
673 MOZ_ASSERT(!elem->mIsSentinel)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!elem->mIsSentinel)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!elem->mIsSentinel))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("!elem->mIsSentinel"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 673); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!elem->mIsSentinel"
")"); do { MOZ_CrashSequence(__null, 673); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
674 }
675
676 /* Check that the mNext/mPrev pointers match up. */
677 const LinkedListElement<T>* prev = &sentinel;
678 const LinkedListElement<T>* cur = sentinel.mNext;
679 do {
680 MOZ_ASSERT(cur->mPrev == prev)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cur->mPrev == prev)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(cur->mPrev == prev))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("cur->mPrev == prev"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 680); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cur->mPrev == prev"
")"); do { MOZ_CrashSequence(__null, 680); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
681 MOZ_ASSERT(prev->mNext == cur)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(prev->mNext == cur)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(prev->mNext == cur))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("prev->mNext == cur"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 681); AnnotateMozCrashReason("MOZ_ASSERT" "(" "prev->mNext == cur"
")"); do { MOZ_CrashSequence(__null, 681); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
682
683 prev = cur;
684 cur = cur->mNext;
685 } while (cur != &sentinel);
686# endif /* ifdef DEBUG */
687 }
688
689 private:
690 friend class LinkedListElement<T>;
691
692 void assertContains(const RawType aValue) const {
693# ifdef DEBUG1
694 for (ConstRawType elem = getFirst(); elem; elem = elem->getNext()) {
695 if (elem == aValue) {
696 return;
697 }
698 }
699 MOZ_CRASH("element wasn't found in this list!")do { do { } while (false); MOZ_ReportCrash("" "element wasn't found in this list!"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/LinkedList.h"
, 699); AnnotateMozCrashReason("MOZ_CRASH(" "element wasn't found in this list!"
")"); do { MOZ_CrashSequence(__null, 699); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
700# endif
701 }
702
703 LinkedList& operator=(const LinkedList<T>& aOther) = delete;
704 LinkedList(const LinkedList<T>& aOther) = delete;
705};
706
707template <typename T>
708size_t RangeSizeEstimate(const LinkedList<T>&) {
709 return 0;
710}
711
712template <typename T>
713inline void ImplCycleCollectionUnlink(LinkedList<RefPtr<T>>& aField) {
714 aField.clear();
715}
716
717template <typename T>
718inline void ImplCycleCollectionTraverse(
719 nsCycleCollectionTraversalCallback& aCallback,
720 LinkedList<RefPtr<T>>& aField, const char* aName, uint32_t aFlags = 0) {
721 typedef typename detail::LinkedListElementTraits<T> Traits;
722 typedef typename Traits::RawType RawType;
723 for (RawType element : aField) {
724 // RefPtr is stored as a raw pointer in LinkedList.
725 // So instead of creating a new RefPtr from the raw
726 // pointer (which is not allowed), we simply call
727 // CycleCollectionNoteChild against the raw pointer
728 CycleCollectionNoteChild(aCallback, element, aName, aFlags);
729 }
730}
731
732template <typename T>
733class AutoCleanLinkedList : public LinkedList<T> {
734 private:
735 using Traits = detail::LinkedListElementTraits<T>;
736 using ClientType = typename detail::LinkedListElementTraits<T>::ClientType;
737
738 public:
739 AutoCleanLinkedList() = default;
740 AutoCleanLinkedList(AutoCleanLinkedList&&) = default;
741 ~AutoCleanLinkedList() { clear(); }
742
743 AutoCleanLinkedList& operator=(AutoCleanLinkedList&& aOther) = default;
744
745 void clear() {
746 while (ClientType element = this->popFirst()) {
747 Traits::cleanElement(element);
748 }
749 }
750};
751
752} /* namespace mozilla */
753
754#endif /* __cplusplus */
755
756#endif /* mozilla_LinkedList_h */

/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h

1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7/* CRTP refcounting templates. Do not use unless you are an Expert. */
8
9#ifndef mozilla_RefCounted_h
10#define mozilla_RefCounted_h
11
12#include <utility>
13#include <type_traits>
14
15#include "mozilla/AlreadyAddRefed.h"
16#include "mozilla/Assertions.h"
17#include "mozilla/Atomics.h"
18#include "mozilla/Attributes.h"
19#include "mozilla/RefCountType.h"
20
21#ifdef __wasi__
22# include "mozilla/WasiAtomic.h"
23#else
24# include <atomic>
25#endif // __wasi__
26
27#if defined(MOZ_SUPPORT_LEAKCHECKING1) && defined(NS_BUILD_REFCNT_LOGGING1)
28# define MOZ_REFCOUNTED_LEAK_CHECKING
29#endif
30
31namespace mozilla {
32
33/**
34 * RefCounted<T> is a sort of a "mixin" for a class T. RefCounted
35 * manages, well, refcounting for T, and because RefCounted is
36 * parameterized on T, RefCounted<T> can call T's destructor directly.
37 * This means T doesn't need to have a virtual dtor and so doesn't
38 * need a vtable.
39 *
40 * RefCounted<T> is created with refcount == 0. Newly-allocated
41 * RefCounted<T> must immediately be assigned to a RefPtr to make the
42 * refcount > 0. It's an error to allocate and free a bare
43 * RefCounted<T>, i.e. outside of the RefPtr machinery. Attempts to
44 * do so will abort DEBUG builds.
45 *
46 * Live RefCounted<T> have refcount > 0. The lifetime (refcounts) of
47 * live RefCounted<T> are controlled by RefPtr<T> and
48 * RefPtr<super/subclass of T>. Upon a transition from refcounted==1
49 * to 0, the RefCounted<T> "dies" and is destroyed. The "destroyed"
50 * state is represented in DEBUG builds by refcount==0xffffdead. This
51 * state distinguishes use-before-ref (refcount==0) from
52 * use-after-destroy (refcount==0xffffdead).
53 *
54 * Note that when deriving from RefCounted or AtomicRefCounted, you
55 * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public
56 * section of your class, where ClassName is the name of your class.
57 */
58namespace detail {
59const MozRefCountType DEAD = 0xffffdead;
60
61#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
62// When this header is included in SpiderMonkey code, NS_LogAddRef and
63// NS_LogRelease are not available. To work around this, we call these
64// functions through a function pointer set by SetLeakCheckingFunctions.
65// Note: these are globals because GCC on Linux reports undefined-reference
66// errors when they're static members of the RefCountLogger class.
67using LogAddRefFunc = void (*)(void* aPtr, MozRefCountType aNewRefCnt,
68 const char* aTypeName, uint32_t aClassSize);
69using LogReleaseFunc = void (*)(void* aPtr, MozRefCountType aNewRefCnt,
70 const char* aTypeName);
71extern MFBT_DATA__attribute__((weak)) __attribute__((visibility("default"))) LogAddRefFunc gLogAddRefFunc;
72extern MFBT_DATA__attribute__((weak)) __attribute__((visibility("default"))) LogReleaseFunc gLogReleaseFunc;
73extern MFBT_DATA__attribute__((weak)) __attribute__((visibility("default"))) size_t gNumStaticCtors;
74extern MFBT_DATA__attribute__((weak)) __attribute__((visibility("default"))) const char* gLastStaticCtorTypeName;
75#endif
76
77// When building code that gets compiled into Gecko, try to use the
78// trace-refcount leak logging facilities.
79class RefCountLogger {
80 public:
81 // Called by `RefCounted`-like classes to log a successful AddRef call in the
82 // Gecko leak-logging system. This call is a no-op outside of Gecko. Should be
83 // called afer incrementing the reference count.
84 template <class T>
85 static void logAddRef(const T* aPointer, MozRefCountType aRefCount) {
86#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
87 const void* pointer = aPointer;
88 const char* typeName = aPointer->typeName();
89 uint32_t typeSize = aPointer->typeSize();
90 if (gLogAddRefFunc) {
91 gLogAddRefFunc(const_cast<void*>(pointer), aRefCount, typeName, typeSize);
92 } else {
93 gNumStaticCtors++;
94 gLastStaticCtorTypeName = typeName;
95 }
96#endif
97 }
98
99#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
100 static MFBT_API__attribute__((weak)) __attribute__((visibility("default"))) void SetLeakCheckingFunctions(LogAddRefFunc aLogAddRefFunc,
101 LogReleaseFunc aLogReleaseFunc);
102#endif
103
104 // Created by `RefCounted`-like classes to log a successful Release call in
105 // the Gecko leak-logging system. The constructor should be invoked before the
106 // refcount is decremented to avoid invoking `typeName()` with a zero
107 // reference count. This call is a no-op outside of Gecko.
108 class MOZ_STACK_CLASS ReleaseLogger final {
109 public:
110 template <class T>
111 explicit ReleaseLogger(const T* aPointer)
112#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
113 : mPointer(aPointer),
114 mTypeName(aPointer->typeName())
115#endif
116 {
117 }
118
119 void logRelease(MozRefCountType aRefCount) {
120#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
121 MOZ_ASSERT(aRefCount != DEAD)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aRefCount != DEAD)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aRefCount != DEAD))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("aRefCount != DEAD"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h"
, 121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aRefCount != DEAD"
")"); do { MOZ_CrashSequence(__null, 121); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
122 if (gLogReleaseFunc) {
123 gLogReleaseFunc(const_cast<void*>(mPointer), aRefCount, mTypeName);
124 } else {
125 gNumStaticCtors++;
126 gLastStaticCtorTypeName = mTypeName;
127 }
128#endif
129 }
130
131#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
132 const void* mPointer;
133 const char* mTypeName;
134#endif
135 };
136};
137
138// This is used WeakPtr.h as well as this file.
139enum RefCountAtomicity { AtomicRefCount, NonAtomicRefCount };
140
141template <typename T, RefCountAtomicity Atomicity>
142class RC {
143 public:
144 explicit RC(T aCount) : mValue(aCount) {}
145
146 RC(const RC&) = delete;
147 RC& operator=(const RC&) = delete;
148 RC(RC&&) = delete;
149 RC& operator=(RC&&) = delete;
150
151 T operator++() { return ++mValue; }
152 T operator--() { return --mValue; }
153
154#ifdef DEBUG1
155 void operator=(const T& aValue) { mValue = aValue; }
156#endif
157
158 operator T() const { return mValue; }
159
160 private:
161 T mValue;
162};
163
164template <typename T>
165class RC<T, AtomicRefCount> {
166 public:
167 explicit RC(T aCount) : mValue(aCount) {}
168
169 RC(const RC&) = delete;
170 RC& operator=(const RC&) = delete;
171 RC(RC&&) = delete;
172 RC& operator=(RC&&) = delete;
173
174 T operator++() {
175 // Memory synchronization is not required when incrementing a
176 // reference count. The first increment of a reference count on a
177 // thread is not important, since the first use of the object on a
178 // thread can happen before it. What is important is the transfer
179 // of the pointer to that thread, which may happen prior to the
180 // first increment on that thread. The necessary memory
181 // synchronization is done by the mechanism that transfers the
182 // pointer between threads.
183 return mValue.fetch_add(1, std::memory_order_relaxed) + 1;
184 }
185
186 T operator--() {
187 // Since this may be the last release on this thread, we need
188 // release semantics so that prior writes on this thread are visible
189 // to the thread that destroys the object when it reads mValue with
190 // acquire semantics.
191 T result = mValue.fetch_sub(1, std::memory_order_release) - 1;
192 if (result == 0) {
193 // We're going to destroy the object on this thread, so we need
194 // acquire semantics to synchronize with the memory released by
195 // the last release on other threads, that is, to ensure that
196 // writes prior to that release are now visible on this thread.
197#if defined(MOZ_TSAN) || defined(__wasi__)
198 // TSan doesn't understand std::atomic_thread_fence, so in order
199 // to avoid a false positive for every time a refcounted object
200 // is deleted, we replace the fence with an atomic operation.
201 mValue.load(std::memory_order_acquire);
202#else
203 std::atomic_thread_fence(std::memory_order_acquire);
204#endif
205 }
206 return result;
207 }
208
209#ifdef DEBUG1
210 // This method is only called in debug builds, so we're not too concerned
211 // about its performance.
212 void operator=(const T& aValue) {
213 mValue.store(aValue, std::memory_order_seq_cst);
214 }
215#endif
216
217 operator T() const {
218 // Use acquire semantics since we're not sure what the caller is
219 // doing.
220 return mValue.load(std::memory_order_acquire);
221 }
222
223 T IncrementIfNonzero() {
224 // This can be a relaxed load as any write of 0 that we observe will leave
225 // the field in a permanently zero (or `DEAD`) state (so a "stale" read of 0
226 // is fine), and any other value is confirmed by the CAS below.
227 //
228 // This roughly matches rust's Arc::upgrade implementation as of rust 1.49.0
229 T prev = mValue.load(std::memory_order_relaxed);
230 while (prev != 0) {
231 MOZ_ASSERT(prev != detail::DEAD,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(prev != detail::DEAD)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(prev != detail::DEAD))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("prev != detail::DEAD"
" (" "Cannot IncrementIfNonzero if marked as dead!" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h"
, 232); AnnotateMozCrashReason("MOZ_ASSERT" "(" "prev != detail::DEAD"
") (" "Cannot IncrementIfNonzero if marked as dead!" ")"); do
{ MOZ_CrashSequence(__null, 232); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
232 "Cannot IncrementIfNonzero if marked as dead!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(prev != detail::DEAD)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(prev != detail::DEAD))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("prev != detail::DEAD"
" (" "Cannot IncrementIfNonzero if marked as dead!" ")", "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h"
, 232); AnnotateMozCrashReason("MOZ_ASSERT" "(" "prev != detail::DEAD"
") (" "Cannot IncrementIfNonzero if marked as dead!" ")"); do
{ MOZ_CrashSequence(__null, 232); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
233 // TODO: It may be possible to use relaxed success ordering here?
234 if (mValue.compare_exchange_weak(prev, prev + 1,
235 std::memory_order_acquire,
236 std::memory_order_relaxed)) {
237 return prev + 1;
238 }
239 }
240 return 0;
241 }
242
243 private:
244 std::atomic<T> mValue;
245};
246
247template <typename T, RefCountAtomicity Atomicity>
248class RefCounted {
249 protected:
250 RefCounted() : mRefCnt(0) {}
251#ifdef DEBUG1
252 ~RefCounted() { MOZ_ASSERT(mRefCnt == detail::DEAD)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRefCnt == detail::DEAD)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRefCnt == detail::DEAD))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("mRefCnt == detail::DEAD"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h"
, 252); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRefCnt == detail::DEAD"
")"); do { MOZ_CrashSequence(__null, 252); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
; }
253#endif
254
255 public:
256 // Compatibility with RefPtr.
257 void AddRef() const {
258 // Note: this method must be thread safe for AtomicRefCounted.
259 MOZ_ASSERT(int32_t(mRefCnt) >= 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(int32_t(mRefCnt) >= 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h"
, 259); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0"
")"); do { MOZ_CrashSequence(__null, 259); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
260 MozRefCountType cnt = ++mRefCnt;
261 detail::RefCountLogger::logAddRef(static_cast<const T*>(this), cnt);
262 }
263
264 void Release() const {
265 // Note: this method must be thread safe for AtomicRefCounted.
266 MOZ_ASSERT(int32_t(mRefCnt) > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(int32_t(mRefCnt) > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h"
, 266); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0"
")"); do { MOZ_CrashSequence(__null, 266); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16
Assuming the condition is true
17
Taking false branch
18
Loop condition is false. Exiting loop
267 detail::RefCountLogger::ReleaseLogger logger(static_cast<const T*>(this));
268 MozRefCountType cnt = --mRefCnt;
269 // Note: it's not safe to touch |this| after decrementing the refcount,
270 // except for below.
271 logger.logRelease(cnt);
272 if (0 == cnt) {
19
Assuming 'cnt' is equal to 0
20
Taking true branch
273 // Because we have atomically decremented the refcount above, only
274 // one thread can get a 0 count here, so as long as we can assume that
275 // everything else in the system is accessing this object through
276 // RefPtrs, it's safe to access |this| here.
277#ifdef DEBUG1
278 mRefCnt = detail::DEAD;
279#endif
280 delete static_cast<const T*>(this);
21
Calling 'operator delete'
23
Returning from 'operator delete'
281 }
282 }
283
284 using HasThreadSafeRefCnt =
285 std::integral_constant<bool, Atomicity == AtomicRefCount>;
286
287 // Compatibility with wtf::RefPtr.
288 void ref() { AddRef(); }
289 void deref() { Release(); }
290 MozRefCountType refCount() const { return mRefCnt; }
291 bool hasOneRef() const {
292 MOZ_ASSERT(mRefCnt > 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mRefCnt > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mRefCnt > 0))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("mRefCnt > 0"
, "/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/RefCounted.h"
, 292); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mRefCnt > 0"
")"); do { MOZ_CrashSequence(__null, 292); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
293 return mRefCnt == 1;
294 }
295
296 private:
297 mutable RC<MozRefCountType, Atomicity> mRefCnt;
298};
299
300#ifdef MOZ_REFCOUNTED_LEAK_CHECKING
301// Passing override for the optional argument marks the typeName and
302// typeSize functions defined by this macro as overrides.
303# define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...)virtual const char* typeName() const ... { return "T"; } virtual
size_t typeSize() const ... { return sizeof(*this); }
\
304 virtual const char* typeName() const __VA_ARGS__ { return #T; } \
305 virtual size_t typeSize() const __VA_ARGS__ { return sizeof(*this); }
306#else
307# define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T, ...)virtual const char* typeName() const ... { return "T"; } virtual
size_t typeSize() const ... { return sizeof(*this); }
308#endif
309
310// Note that this macro is expanded unconditionally because it declares only
311// two small inline functions which will hopefully get eliminated by the linker
312// in non-leak-checking builds.
313#define MOZ_DECLARE_REFCOUNTED_TYPENAME(T)const char* typeName() const { return "T"; } size_t typeSize(
) const { return sizeof(*this); }
\
314 const char* typeName() const { return #T; } \
315 size_t typeSize() const { return sizeof(*this); }
316
317} // namespace detail
318
319template <typename T>
320class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount> {
321 public:
322 ~RefCounted() {
323 static_assert(std::is_base_of<RefCounted, T>::value,
324 "T must derive from RefCounted<T>");
325 }
326};
327
328namespace external {
329
330/**
331 * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
332 * reference counter.
333 *
334 * NOTE: Please do not use this class, use NS_INLINE_DECL_THREADSAFE_REFCOUNTING
335 * instead.
336 */
337template <typename T>
338class AtomicRefCounted
339 : public mozilla::detail::RefCounted<T, mozilla::detail::AtomicRefCount> {
340 public:
341 ~AtomicRefCounted() {
342 static_assert(std::is_base_of<AtomicRefCounted, T>::value,
343 "T must derive from AtomicRefCounted<T>");
344 }
345};
346
347} // namespace external
348
349} // namespace mozilla
350
351#endif // mozilla_RefCounted_h

/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/cxxalloc.h

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#ifndef mozilla_cxxalloc_h
6#define mozilla_cxxalloc_h
7
8/*
9 * We implement the default operators new/delete as part of
10 * libmozalloc, replacing their definitions in libstdc++. The
11 * operator new* definitions in libmozalloc will never return a NULL
12 * pointer.
13 *
14 * Each operator new immediately below returns a pointer to memory
15 * that can be delete'd by any of
16 *
17 * (1) the matching infallible operator delete immediately below
18 * (2) the matching system |operator delete(void*, std::nothrow)|
19 * (3) the matching system |operator delete(void*) noexcept(false)|
20 *
21 * NB: these are declared |noexcept(false)|, though they will never
22 * throw that exception. This declaration is consistent with the rule
23 * that |::operator new() noexcept(false)| will never return NULL.
24 *
25 * NB: mozilla::fallible can be used instead of std::nothrow.
26 */
27
28#ifndef MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline
29# define MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline MFBT_API__attribute__((weak)) __attribute__((visibility("default")))
30#endif
31
32MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new(size_t size) noexcept(false) {
33 return moz_xmalloc(size);
34}
35
36MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new(size_t size,
37 const std::nothrow_t&) noexcept(true) {
38 return malloc_implmalloc(size);
39}
40
41MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new[](size_t size) noexcept(false) {
42 return moz_xmalloc(size);
43}
44
45// Inlining `new` like this is technically against C++ spec, but we crave perf.
46MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new[](size_t size,
47 const std::nothrow_t&) noexcept(true) {
48#ifdef __GNUC__4
49// GCC-14 codegen at -O2 causes false positive due to converting
50// `new A[n]` to `malloc(-1)` when `n > PTRDIFF_MAX/sizeof(A)`.
51// (See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85783, WONTFIX'd)
52# pragma GCC diagnostic push
53# pragma GCC diagnostic ignored "-Walloc-size-larger-than="
54#endif
55
56 return malloc_implmalloc(size);
57
58#ifdef __GNUC__4
59# pragma GCC diagnostic pop
60#endif
61}
62
63MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr) noexcept(true) {
64 return free_implfree(ptr);
22
Memory is released
65}
66
67MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr,
68 const std::nothrow_t&) noexcept(true) {
69 return free_implfree(ptr);
70}
71
72MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete[](void* ptr) noexcept(true) {
73 return free_implfree(ptr);
74}
75
76MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete[](
77 void* ptr, const std::nothrow_t&) noexcept(true) {
78 return free_implfree(ptr);
79}
80
81#if defined(XP_WIN)
82// We provide the global sized delete overloads unconditionally because the
83// MSVC runtime headers do, despite compiling with /Zc:sizedDealloc-
84MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr,
85 size_t /*size*/) noexcept(true) {
86 return free_implfree(ptr);
87}
88
89MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete[](void* ptr,
90 size_t /*size*/) noexcept(true) {
91 return free_implfree(ptr);
92}
93#endif
94
95#endif /* mozilla_cxxalloc_h */