1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/WebGPUBinding.h"
7 #include "CanvasContext.h"
9 #include "LayerUserData.h"
10 #include "nsDisplayList.h"
11 #include "mozilla/dom/HTMLCanvasElement.h"
12 #include "mozilla/gfx/CanvasManagerChild.h"
13 #include "mozilla/layers/CanvasRenderer.h"
14 #include "mozilla/layers/CompositableForwarder.h"
15 #include "mozilla/layers/ImageDataSerializer.h"
16 #include "mozilla/layers/LayersSurfaces.h"
17 #include "mozilla/layers/RenderRootStateManager.h"
18 #include "mozilla/layers/WebRenderCanvasRenderer.h"
19 #include "mozilla/StaticPrefs_privacy.h"
20 #include "mozilla/SVGObserverUtils.h"
21 #include "ipc/WebGPUChild.h"
26 inline void ImplCycleCollectionTraverse(
27 nsCycleCollectionTraversalCallback
& aCallback
,
28 dom::GPUCanvasConfiguration
& aField
, const char* aName
, uint32_t aFlags
) {
29 aField
.TraverseForCC(aCallback
, aFlags
);
32 inline void ImplCycleCollectionUnlink(dom::GPUCanvasConfiguration
& aField
) {
39 inline void ImplCycleCollectionTraverse(
40 nsCycleCollectionTraversalCallback
& aCallback
,
41 const std::unique_ptr
<T
>& aField
, const char* aName
, uint32_t aFlags
) {
43 ImplCycleCollectionTraverse(aCallback
, *aField
, aName
, aFlags
);
48 inline void ImplCycleCollectionUnlink(std::unique_ptr
<T
>& aField
) {
52 } // namespace mozilla
56 namespace mozilla::webgpu
{
58 NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasContext
)
59 NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasContext
)
61 GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(CanvasContext
, mConfig
,
66 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasContext
)
67 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
68 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal
)
69 NS_INTERFACE_MAP_ENTRY(nsISupports
)
74 CanvasContext::CanvasContext() = default;
76 CanvasContext::~CanvasContext() {
78 RemovePostRefreshObserver();
81 void CanvasContext::Cleanup() { Unconfigure(); }
83 JSObject
* CanvasContext::WrapObject(JSContext
* aCx
,
84 JS::Handle
<JSObject
*> aGivenProto
) {
85 return dom::GPUCanvasContext_Binding::Wrap(aCx
, this, aGivenProto
);
90 void CanvasContext::GetCanvas(
91 dom::OwningHTMLCanvasElementOrOffscreenCanvas
& aRetVal
) const {
93 aRetVal
.SetAsHTMLCanvasElement() = mCanvasElement
;
94 } else if (mOffscreenCanvas
) {
95 aRetVal
.SetAsOffscreenCanvas() = mOffscreenCanvas
;
98 "This should only happen briefly during CC Unlink, and no JS should "
103 void CanvasContext::Configure(const dom::GPUCanvasConfiguration
& aConfig
) {
106 // Bug 1864904: Failures in validation should throw a TypeError, per spec.
108 // these formats are guaranteed by the spec
109 switch (aConfig
.mFormat
) {
110 case dom::GPUTextureFormat::Rgba8unorm
:
111 case dom::GPUTextureFormat::Rgba8unorm_srgb
:
112 mGfxFormat
= gfx::SurfaceFormat::R8G8B8A8
;
114 case dom::GPUTextureFormat::Bgra8unorm
:
115 case dom::GPUTextureFormat::Bgra8unorm_srgb
:
116 mGfxFormat
= gfx::SurfaceFormat::B8G8R8A8
;
119 NS_WARNING("Specified swap chain format is not supported");
123 mConfig
.reset(new dom::GPUCanvasConfiguration(aConfig
));
124 mRemoteTextureOwnerId
= Some(layers::RemoteTextureOwnerId::GetNext());
125 mUseExternalTextureInSwapChain
=
126 wgpu_client_use_external_texture_in_swapChain(
127 aConfig
.mDevice
->mId
, ConvertTextureFormat(aConfig
.mFormat
));
128 mTexture
= aConfig
.mDevice
->InitSwapChain(
129 mConfig
.get(), mRemoteTextureOwnerId
.ref(),
130 mUseExternalTextureInSwapChain
, mGfxFormat
, mCanvasSize
);
136 mTexture
->mTargetContext
= this;
137 mBridge
= aConfig
.mDevice
->GetBridge();
138 if (mCanvasElement
) {
139 mWaitingCanvasRendererInitialized
= true;
145 void CanvasContext::Unconfigure() {
146 if (mBridge
&& mBridge
->IsOpen() && mRemoteTextureOwnerId
) {
147 mBridge
->SendSwapChainDrop(
148 *mRemoteTextureOwnerId
,
149 layers::ToRemoteTextureTxnType(mFwdTransactionTracker
),
150 layers::ToRemoteTextureTxnId(mFwdTransactionTracker
));
152 mRemoteTextureOwnerId
= Nothing();
153 mFwdTransactionTracker
= nullptr;
157 mGfxFormat
= gfx::SurfaceFormat::UNKNOWN
;
160 NS_IMETHODIMP
CanvasContext::SetDimensions(int32_t aWidth
, int32_t aHeight
) {
161 aWidth
= std::max(1, aWidth
);
162 aHeight
= std::max(1, aHeight
);
163 const auto newSize
= gfx::IntSize
{aWidth
, aHeight
};
164 if (newSize
== mCanvasSize
) return NS_OK
; // No-op no-change resizes.
166 mCanvasSize
= newSize
;
168 const auto copy
= dom::GPUCanvasConfiguration
{
169 *mConfig
}; // So we can't null it out on ourselves.
175 RefPtr
<Texture
> CanvasContext::GetCurrentTexture(ErrorResult
& aRv
) {
177 aRv
.ThrowOperationError("Canvas not configured");
182 MOZ_ASSERT(mRemoteTextureOwnerId
.isSome());
184 if (mNewTextureRequested
) {
185 mNewTextureRequested
= false;
187 mTexture
= mConfig
->mDevice
->CreateTextureForSwapChain(
188 mConfig
.get(), mCanvasSize
, mRemoteTextureOwnerId
.ref());
189 mTexture
->mTargetContext
= this;
194 void CanvasContext::MaybeQueueSwapChainPresent() {
195 MOZ_ASSERT(mTexture
);
198 mBridge
->NotifyWaitForSubmit(mTexture
->mId
);
201 if (mPendingSwapChainPresent
) {
205 mPendingSwapChainPresent
= true;
207 if (mWaitingCanvasRendererInitialized
) {
211 InvalidateCanvasContent();
214 Maybe
<layers::SurfaceDescriptor
> CanvasContext::SwapChainPresent() {
215 mPendingSwapChainPresent
= false;
216 if (!mBridge
|| !mBridge
->IsOpen() || mRemoteTextureOwnerId
.isNothing() ||
220 mLastRemoteTextureId
= Some(layers::RemoteTextureId::GetNext());
221 mBridge
->SwapChainPresent(mTexture
->mId
, *mLastRemoteTextureId
,
222 *mRemoteTextureOwnerId
);
223 if (mUseExternalTextureInSwapChain
) {
225 mNewTextureRequested
= true;
227 return Some(layers::SurfaceDescriptorRemoteTexture(*mLastRemoteTextureId
,
228 *mRemoteTextureOwnerId
));
231 bool CanvasContext::UpdateWebRenderCanvasData(
232 mozilla::nsDisplayListBuilder
* aBuilder
, WebRenderCanvasData
* aCanvasData
) {
233 auto* renderer
= aCanvasData
->GetCanvasRenderer();
235 if (renderer
&& mRemoteTextureOwnerId
.isSome() &&
236 renderer
->GetRemoteTextureOwnerId() == mRemoteTextureOwnerId
) {
240 renderer
= aCanvasData
->CreateCanvasRenderer();
241 if (!InitializeCanvasRenderer(aBuilder
, renderer
)) {
242 // Clear CanvasRenderer of WebRenderCanvasData
243 aCanvasData
->ClearCanvasRenderer();
249 bool CanvasContext::InitializeCanvasRenderer(
250 nsDisplayListBuilder
* aBuilder
, layers::CanvasRenderer
* aRenderer
) {
251 if (mRemoteTextureOwnerId
.isNothing()) {
255 layers::CanvasRendererData data
;
256 data
.mContext
= this;
257 data
.mSize
= mCanvasSize
;
258 data
.mIsOpaque
= false;
259 data
.mRemoteTextureOwnerId
= mRemoteTextureOwnerId
;
261 aRenderer
->Initialize(data
);
262 aRenderer
->SetDirty();
264 if (mWaitingCanvasRendererInitialized
) {
265 InvalidateCanvasContent();
267 mWaitingCanvasRendererInitialized
= false;
272 mozilla::UniquePtr
<uint8_t[]> CanvasContext::GetImageBuffer(
273 int32_t* out_format
, gfx::IntSize
* out_imageSize
) {
278 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
283 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
284 *out_imageSize
= dataSurface
->GetSize();
286 if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization
)) {
287 gfxUtils::GetImageBufferWithRandomNoise(
289 /* aIsAlphaPremultiplied */ true, GetCookieJarSettings(), &*out_format
);
292 return gfxUtils::GetImageBuffer(dataSurface
, /* aIsAlphaPremultiplied */ true,
296 NS_IMETHODIMP
CanvasContext::GetInputStream(const char* aMimeType
,
297 const nsAString
& aEncoderOptions
,
298 nsIInputStream
** aStream
) {
300 RefPtr
<gfx::SourceSurface
> snapshot
= GetSurfaceSnapshot(&any
);
302 return NS_ERROR_FAILURE
;
305 RefPtr
<gfx::DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
307 if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization
)) {
308 gfxUtils::GetInputStreamWithRandomNoise(
309 dataSurface
, /* aIsAlphaPremultiplied */ true, aMimeType
,
310 aEncoderOptions
, GetCookieJarSettings(), aStream
);
313 return gfxUtils::GetInputStream(dataSurface
, /* aIsAlphaPremultiplied */ true,
314 aMimeType
, aEncoderOptions
, aStream
);
317 already_AddRefed
<mozilla::gfx::SourceSurface
> CanvasContext::GetSurfaceSnapshot(
318 gfxAlphaType
* aOutAlphaType
) {
320 *aOutAlphaType
= gfxAlphaType::Premult
;
323 auto* const cm
= gfx::CanvasManagerChild::Get();
328 if (!mBridge
|| !mBridge
->IsOpen() || mRemoteTextureOwnerId
.isNothing()) {
332 MOZ_ASSERT(mRemoteTextureOwnerId
.isSome());
333 return cm
->GetSnapshot(cm
->Id(), mBridge
->Id(), mRemoteTextureOwnerId
,
334 mGfxFormat
, /* aPremultiply */ false,
338 Maybe
<layers::SurfaceDescriptor
> CanvasContext::GetFrontBuffer(
339 WebGLFramebufferJS
*, const bool) {
340 if (mPendingSwapChainPresent
) {
341 auto desc
= SwapChainPresent();
342 MOZ_ASSERT(!mPendingSwapChainPresent
);
348 already_AddRefed
<layers::FwdTransactionTracker
>
349 CanvasContext::UseCompositableForwarder(
350 layers::CompositableForwarder
* aForwarder
) {
351 return layers::FwdTransactionTracker::GetOrCreate(mFwdTransactionTracker
);
354 void CanvasContext::ForceNewFrame() {
355 if (!mCanvasElement
&& !mOffscreenCanvas
) {
359 // Force a new frame to be built, which will execute the
360 // `CanvasContextType::WebGPU` switch case in `CreateWebRenderCommands` and
361 // populate the WR user data.
362 if (mCanvasElement
) {
363 mCanvasElement
->InvalidateCanvas();
364 } else if (mOffscreenCanvas
) {
365 dom::OffscreenCanvasDisplayData data
;
366 data
.mSize
= mCanvasSize
;
367 data
.mIsOpaque
= false;
368 data
.mOwnerId
= mRemoteTextureOwnerId
;
369 mOffscreenCanvas
->UpdateDisplayData(data
);
373 void CanvasContext::InvalidateCanvasContent() {
374 if (!mCanvasElement
&& !mOffscreenCanvas
) {
375 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
379 if (mCanvasElement
) {
380 SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement
);
381 mCanvasElement
->InvalidateCanvasContent(nullptr);
382 } else if (mOffscreenCanvas
) {
383 mOffscreenCanvas
->QueueCommitToCompositor();
387 } // namespace mozilla::webgpu