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/. */
5 
6 #include "mozilla/dom/WebGPUBinding.h"
7 #include "CanvasContext.h"
8 #include "SwapChain.h"
9 #include "nsDisplayList.h"
10 #include "LayerUserData.h"
11 #include "mozilla/dom/HTMLCanvasElement.h"
12 #include "mozilla/layers/CompositorManagerChild.h"
13 #include "mozilla/layers/RenderRootStateManager.h"
14 #include "mozilla/layers/WebRenderBridgeChild.h"
15 #include "ipc/WebGPUChild.h"
16 
17 namespace mozilla {
18 namespace webgpu {
19 
20 NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasContext)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasContext)21 NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasContext)
22 
23 GPU_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasContext, mSwapChain,
24                                        mCanvasElement, mOffscreenCanvas)
25 
26 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasContext)
27   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
28   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
29   NS_INTERFACE_MAP_ENTRY(nsISupports)
30 NS_INTERFACE_MAP_END
31 
32 CanvasContext::CanvasContext()
33     : mExternalImageId(layers::CompositorManagerChild::GetInstance()
34                            ->GetNextExternalImageId()) {}
35 
~CanvasContext()36 CanvasContext::~CanvasContext() {
37   Cleanup();
38   RemovePostRefreshObserver();
39 }
40 
Cleanup()41 void CanvasContext::Cleanup() {
42   if (mSwapChain) {
43     mSwapChain->Destroy(mExternalImageId);
44     mSwapChain = nullptr;
45   }
46   if (mRenderRootStateManager && mImageKey) {
47     mRenderRootStateManager->AddImageKeyForDiscard(mImageKey.value());
48     mRenderRootStateManager = nullptr;
49     mImageKey.reset();
50   }
51 }
52 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)53 JSObject* CanvasContext::WrapObject(JSContext* aCx,
54                                     JS::Handle<JSObject*> aGivenProto) {
55   return dom::GPUCanvasContext_Binding::Wrap(aCx, this, aGivenProto);
56 }
57 
GetCanvasLayer(nsDisplayListBuilder * aBuilder,layers::Layer * aOldLayer,layers::LayerManager * aManager)58 already_AddRefed<layers::Layer> CanvasContext::GetCanvasLayer(
59     nsDisplayListBuilder* aBuilder, layers::Layer* aOldLayer,
60     layers::LayerManager* aManager) {
61   return nullptr;
62 }
63 
UpdateWebRenderCanvasData(nsDisplayListBuilder * aBuilder,WebRenderCanvasData * aCanvasData)64 bool CanvasContext::UpdateWebRenderCanvasData(
65     nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) {
66   return true;
67 }
68 
GetSwapChainPreferredFormat(Adapter &) const69 dom::GPUTextureFormat CanvasContext::GetSwapChainPreferredFormat(
70     Adapter&) const {
71   return dom::GPUTextureFormat::Bgra8unorm;
72 }
73 
ConfigureSwapChain(const dom::GPUSwapChainDescriptor & aDesc,ErrorResult & aRv)74 RefPtr<SwapChain> CanvasContext::ConfigureSwapChain(
75     const dom::GPUSwapChainDescriptor& aDesc, ErrorResult& aRv) {
76   Cleanup();
77 
78   gfx::SurfaceFormat format;
79   switch (aDesc.mFormat) {
80     case dom::GPUTextureFormat::Rgba8unorm:
81       format = gfx::SurfaceFormat::R8G8B8A8;
82       break;
83     case dom::GPUTextureFormat::Bgra8unorm:
84       format = gfx::SurfaceFormat::B8G8R8A8;
85       break;
86     default:
87       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
88       return nullptr;
89   }
90 
91   dom::GPUExtent3DDict extent;
92   extent.mWidth = mWidth;
93   extent.mHeight = mHeight;
94   extent.mDepthOrArrayLayers = 1;
95   mSwapChain = new SwapChain(aDesc, extent, mExternalImageId, format);
96 
97   // Force a new frame to be built, which will execute the
98   // `CanvasContextType::WebGPU` switch case in `CreateWebRenderCommands` and
99   // populate the WR user data.
100   mCanvasElement->InvalidateCanvas();
101 
102   mSwapChain->GetCurrentTexture()->mTargetCanvasElement = mCanvasElement;
103   return mSwapChain;
104 }
105 
GetImageKey() const106 Maybe<wr::ImageKey> CanvasContext::GetImageKey() const { return mImageKey; }
107 
CreateImageKey(layers::RenderRootStateManager * aManager)108 wr::ImageKey CanvasContext::CreateImageKey(
109     layers::RenderRootStateManager* aManager) {
110   const auto key = aManager->WrBridge()->GetNextImageKey();
111   mRenderRootStateManager = aManager;
112   mImageKey = Some(key);
113   return key;
114 }
115 
UpdateWebRenderLocalCanvasData(layers::WebRenderLocalCanvasData * aCanvasData)116 bool CanvasContext::UpdateWebRenderLocalCanvasData(
117     layers::WebRenderLocalCanvasData* aCanvasData) {
118   if (!mSwapChain || !mSwapChain->GetParent()) {
119     return false;
120   }
121 
122   const auto size =
123       nsIntSize(AssertedCast<int>(mWidth), AssertedCast<int>(mHeight));
124   if (mSwapChain->mSize != size) {
125     const auto gfxFormat = mSwapChain->mGfxFormat;
126     dom::GPUSwapChainDescriptor desc;
127     desc.mFormat = static_cast<dom::GPUTextureFormat>(mSwapChain->mFormat);
128     desc.mUsage = mSwapChain->mUsage;
129     desc.mDevice = mSwapChain->GetParent();
130 
131     mSwapChain->Destroy(mExternalImageId);
132     mExternalImageId =
133         layers::CompositorManagerChild::GetInstance()->GetNextExternalImageId();
134 
135     dom::GPUExtent3DDict extent;
136     extent.mWidth = size.width;
137     extent.mHeight = size.height;
138     extent.mDepthOrArrayLayers = 1;
139     mSwapChain = new SwapChain(desc, extent, mExternalImageId, gfxFormat);
140   }
141 
142   aCanvasData->mGpuBridge = mSwapChain->GetParent()->GetBridge().get();
143   aCanvasData->mGpuTextureId = mSwapChain->GetCurrentTexture()->mId;
144   aCanvasData->mExternalImageId = mExternalImageId;
145   aCanvasData->mFormat = mSwapChain->mGfxFormat;
146   return true;
147 }
148 
149 }  // namespace webgpu
150 }  // namespace mozilla
151