1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/modules/webgpu/gpu.h"
6 
7 #include <utility>
8 
9 #include "gpu/command_buffer/client/webgpu_interface.h"
10 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
11 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
12 #include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
13 #include "third_party/blink/public/platform/platform.h"
14 #include "third_party/blink/public/platform/web_graphics_context_3d_provider.h"
15 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
16 #include "third_party/blink/renderer/bindings/modules/v8/v8_gpu_request_adapter_options.h"
17 #include "third_party/blink/renderer/core/dom/dom_exception.h"
18 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
19 #include "third_party/blink/renderer/core/inspector/console_message.h"
20 #include "third_party/blink/renderer/modules/webgpu/gpu_adapter.h"
21 #include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
22 #include "third_party/blink/renderer/platform/heap/heap.h"
23 #include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
24 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
25 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
26 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
27 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
28 
29 namespace blink {
30 
31 namespace {
32 
CreateContextProvider(const KURL & url,base::WaitableEvent * waitable_event,std::unique_ptr<WebGraphicsContext3DProvider> * created_context_provider)33 void CreateContextProvider(
34     const KURL& url,
35     base::WaitableEvent* waitable_event,
36     std::unique_ptr<WebGraphicsContext3DProvider>* created_context_provider) {
37   DCHECK(IsMainThread());
38   *created_context_provider =
39       Platform::Current()->CreateWebGPUGraphicsContext3DProvider(url);
40   waitable_event->Signal();
41 }
42 
CreateContextProviderOnMainThread(const KURL & url)43 std::unique_ptr<WebGraphicsContext3DProvider> CreateContextProviderOnMainThread(
44     const KURL& url) {
45   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
46       Thread::MainThread()->GetTaskRunner();
47 
48   base::WaitableEvent waitable_event;
49   std::unique_ptr<WebGraphicsContext3DProvider> created_context_provider;
50   PostCrossThreadTask(
51       *task_runner, FROM_HERE,
52       CrossThreadBindOnce(&CreateContextProvider, url,
53                           CrossThreadUnretained(&waitable_event),
54                           CrossThreadUnretained(&created_context_provider)));
55 
56   waitable_event.Wait();
57   return created_context_provider;
58 }
59 
CreateContextProvider(ExecutionContext & execution_context)60 std::unique_ptr<WebGraphicsContext3DProvider> CreateContextProvider(
61     ExecutionContext& execution_context) {
62   const KURL& url = execution_context.Url();
63   std::unique_ptr<WebGraphicsContext3DProvider> context_provider;
64   if (IsMainThread()) {
65     context_provider =
66         Platform::Current()->CreateWebGPUGraphicsContext3DProvider(url);
67   } else {
68     context_provider = CreateContextProviderOnMainThread(url);
69   }
70 
71   // TODO(kainino): we will need a better way of accessing the GPU interface
72   // from multiple threads than BindToCurrentThread et al.
73   if (context_provider && !context_provider->BindToCurrentThread()) {
74     // TODO(crbug.com/973017): Collect GPU info and surface context creation
75     // error.
76     return nullptr;
77   }
78   return context_provider;
79 }
80 
81 }  // anonymous namespace
82 
83 // static
Create(ExecutionContext & execution_context)84 GPU* GPU::Create(ExecutionContext& execution_context) {
85   return MakeGarbageCollected<GPU>(execution_context);
86 }
87 
GPU(ExecutionContext & execution_context)88 GPU::GPU(ExecutionContext& execution_context)
89     : ExecutionContextLifecycleObserver(&execution_context) {}
90 
91 GPU::~GPU() = default;
92 
Trace(Visitor * visitor) const93 void GPU::Trace(Visitor* visitor) const {
94   ScriptWrappable::Trace(visitor);
95   ExecutionContextLifecycleObserver::Trace(visitor);
96 }
97 
ContextDestroyed()98 void GPU::ContextDestroyed() {
99   if (!dawn_control_client_) {
100     return;
101   }
102   dawn_control_client_->Destroy();
103 }
104 
OnRequestAdapterCallback(ScriptState * script_state,const GPURequestAdapterOptions * options,ScriptPromiseResolver * resolver,int32_t adapter_server_id,const WGPUDeviceProperties & properties,const char * error_message)105 void GPU::OnRequestAdapterCallback(ScriptState* script_state,
106                                    const GPURequestAdapterOptions* options,
107                                    ScriptPromiseResolver* resolver,
108                                    int32_t adapter_server_id,
109                                    const WGPUDeviceProperties& properties,
110                                    const char* error_message) {
111   GPUAdapter* adapter = nullptr;
112   if (adapter_server_id >= 0) {
113     adapter = MakeGarbageCollected<GPUAdapter>(
114         "Default", adapter_server_id, properties, dawn_control_client_);
115   }
116   if (error_message) {
117     ExecutionContext* execution_context = ExecutionContext::From(script_state);
118     auto* console_message = MakeGarbageCollected<ConsoleMessage>(
119         mojom::blink::ConsoleMessageSource::kRendering,
120         mojom::blink::ConsoleMessageLevel::kWarning, error_message);
121     execution_context->AddConsoleMessage(console_message);
122   }
123   RecordAdapterForIdentifiability(script_state, options, adapter);
124   resolver->Resolve(adapter);
125 }
126 
RecordAdapterForIdentifiability(ScriptState * script_state,const GPURequestAdapterOptions * options,GPUAdapter * adapter) const127 void GPU::RecordAdapterForIdentifiability(
128     ScriptState* script_state,
129     const GPURequestAdapterOptions* options,
130     GPUAdapter* adapter) const {
131   constexpr IdentifiableSurface::Type type =
132       IdentifiableSurface::Type::kGPU_RequestAdapter;
133   if (!IdentifiabilityStudySettings::Get()->ShouldSample(type))
134     return;
135   ExecutionContext* context = GetExecutionContext();
136   if (!context)
137     return;
138 
139   IdentifiableTokenBuilder input_builder;
140   if (options && options->hasPowerPreference()) {
141     input_builder.AddToken(
142         IdentifiabilityBenignStringToken(options->powerPreference()));
143   }
144   const auto surface =
145       IdentifiableSurface::FromTypeAndToken(type, input_builder.GetToken());
146 
147   IdentifiableTokenBuilder output_builder;
148   if (adapter) {
149     output_builder.AddToken(IdentifiabilityBenignStringToken(adapter->name()));
150     for (const auto& extension : adapter->extensions(script_state)) {
151       output_builder.AddToken(IdentifiabilityBenignStringToken(extension));
152     }
153   }
154 
155   IdentifiabilityMetricBuilder(context->UkmSourceID())
156       .Set(surface, output_builder.GetToken())
157       .Record(context->UkmRecorder());
158 }
159 
requestAdapter(ScriptState * script_state,const GPURequestAdapterOptions * options)160 ScriptPromise GPU::requestAdapter(ScriptState* script_state,
161                                   const GPURequestAdapterOptions* options) {
162   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
163   ScriptPromise promise = resolver->Promise();
164 
165   if (!dawn_control_client_ || dawn_control_client_->IsContextLost()) {
166     ExecutionContext* execution_context = ExecutionContext::From(script_state);
167     // TODO(natlee@microsoft.com): if GPU process is lost, wait for the GPU
168     // process to come back instead of rejecting right away
169     std::unique_ptr<WebGraphicsContext3DProvider> context_provider =
170         CreateContextProvider(*execution_context);
171 
172     if (!context_provider) {
173       // Failed to create context provider, won't be able to request adapter
174       // TODO(crbug.com/973017): Collect GPU info and surface context creation
175       // error.
176       resolver->Reject(MakeGarbageCollected<DOMException>(
177           DOMExceptionCode::kOperationError, "Fail to request GPUAdapter"));
178       return promise;
179     } else {
180       // Make a new DawnControlClientHolder with the context provider we just
181       // made and set the lost context callback
182       dawn_control_client_ = base::MakeRefCounted<DawnControlClientHolder>(
183           std::move(context_provider));
184       dawn_control_client_->SetLostContextCallback();
185     }
186   }
187 
188   // For now we choose kHighPerformance by default.
189   gpu::webgpu::PowerPreference power_preference =
190       gpu::webgpu::PowerPreference::kHighPerformance;
191   if (options->hasPowerPreference() &&
192       options->powerPreference() == "low-power") {
193     power_preference = gpu::webgpu::PowerPreference::kLowPower;
194   }
195 
196   if (!dawn_control_client_->GetInterface()->RequestAdapterAsync(
197           power_preference,
198           WTF::Bind(&GPU::OnRequestAdapterCallback, WrapPersistent(this),
199                     WrapPersistent(script_state), WrapPersistent(options),
200                     WrapPersistent(resolver)))) {
201     resolver->Reject(MakeGarbageCollected<DOMException>(
202         DOMExceptionCode::kOperationError, "Fail to request GPUAdapter"));
203   }
204 
205   return promise;
206 }
207 
208 }  // namespace blink
209