1 // Copyright (c) 2012 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 "content/renderer/pepper/ppb_graphics_3d_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/feature_list.h"
10 #include "base/location.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "content/public/common/content_features.h"
16 #include "content/public/common/content_switches.h"
17 #include "content/public/common/gpu_stream_constants.h"
18 #include "content/public/common/web_preferences.h"
19 #include "content/renderer/pepper/host_globals.h"
20 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
21 #include "content/renderer/pepper/plugin_instance_throttler_impl.h"
22 #include "content/renderer/pepper/plugin_module.h"
23 #include "content/renderer/render_thread_impl.h"
24 #include "content/renderer/render_view_impl.h"
25 #include "gpu/GLES2/gl2extchromium.h"
26 #include "gpu/command_buffer/common/context_creation_attribs.h"
27 #include "gpu/ipc/client/command_buffer_proxy_impl.h"
28 #include "gpu/ipc/client/gpu_channel_host.h"
29 #include "ppapi/c/ppp_graphics_3d.h"
30 #include "ppapi/thunk/enter.h"
31 #include "third_party/blink/public/platform/web_string.h"
32 #include "third_party/blink/public/web/web_console_message.h"
33 #include "third_party/blink/public/web/web_document.h"
34 #include "third_party/blink/public/web/web_local_frame.h"
35 #include "third_party/blink/public/web/web_plugin_container.h"
36 #include "third_party/khronos/GLES2/gl2.h"
37 
38 using ppapi::thunk::EnterResourceNoLock;
39 using ppapi::thunk::PPB_Graphics3D_API;
40 using blink::WebConsoleMessage;
41 using blink::WebLocalFrame;
42 using blink::WebPluginContainer;
43 using blink::WebString;
44 
45 namespace content {
46 
PPB_Graphics3D_Impl(PP_Instance instance)47 PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance)
48     : PPB_Graphics3D_Shared(instance),
49       bound_to_instance_(false),
50       commit_pending_(false),
51       has_alpha_(false),
52       use_image_chromium_(
53           !base::CommandLine::ForCurrentProcess()->HasSwitch(
54               switches::kDisablePepper3DImageChromium) &&
55           base::FeatureList::IsEnabled(features::kPepper3DImageChromium)) {}
56 
~PPB_Graphics3D_Impl()57 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
58   // Unset the client before the command_buffer_ is destroyed, similar to how
59   // WeakPtrFactory invalidates before it.
60   if (command_buffer_)
61     command_buffer_->SetGpuControlClient(nullptr);
62 }
63 
64 // static
CreateRaw(PP_Instance instance,PP_Resource share_context,const gpu::ContextCreationAttribs & attrib_helper,gpu::Capabilities * capabilities,const base::UnsafeSharedMemoryRegion ** shared_state_region,gpu::CommandBufferId * command_buffer_id)65 PP_Resource PPB_Graphics3D_Impl::CreateRaw(
66     PP_Instance instance,
67     PP_Resource share_context,
68     const gpu::ContextCreationAttribs& attrib_helper,
69     gpu::Capabilities* capabilities,
70     const base::UnsafeSharedMemoryRegion** shared_state_region,
71     gpu::CommandBufferId* command_buffer_id) {
72   PPB_Graphics3D_API* share_api = nullptr;
73   if (share_context) {
74     EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
75     if (enter.failed())
76       return 0;
77     share_api = enter.object();
78   }
79   scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
80       new PPB_Graphics3D_Impl(instance));
81   if (!graphics_3d->InitRaw(share_api, attrib_helper, capabilities,
82                             shared_state_region, command_buffer_id))
83     return 0;
84   return graphics_3d->GetReference();
85 }
86 
SetGetBuffer(int32_t transfer_buffer_id)87 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
88   GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
89   return PP_TRUE;
90 }
91 
CreateTransferBuffer(uint32_t size,int32_t * id)92 scoped_refptr<gpu::Buffer> PPB_Graphics3D_Impl::CreateTransferBuffer(
93     uint32_t size,
94     int32_t* id) {
95   return GetCommandBuffer()->CreateTransferBuffer(size, id);
96 }
97 
DestroyTransferBuffer(int32_t id)98 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
99   GetCommandBuffer()->DestroyTransferBuffer(id);
100   return PP_TRUE;
101 }
102 
Flush(int32_t put_offset)103 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
104   GetCommandBuffer()->Flush(put_offset);
105   return PP_TRUE;
106 }
107 
WaitForTokenInRange(int32_t start,int32_t end)108 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForTokenInRange(
109     int32_t start,
110     int32_t end) {
111   return GetCommandBuffer()->WaitForTokenInRange(start, end);
112 }
113 
WaitForGetOffsetInRange(uint32_t set_get_buffer_count,int32_t start,int32_t end)114 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForGetOffsetInRange(
115     uint32_t set_get_buffer_count,
116     int32_t start,
117     int32_t end) {
118   return GetCommandBuffer()->WaitForGetOffsetInRange(set_get_buffer_count,
119                                                      start, end);
120 }
121 
EnsureWorkVisible()122 void PPB_Graphics3D_Impl::EnsureWorkVisible() {
123   command_buffer_->EnsureWorkVisible();
124 }
125 
TakeFrontBuffer()126 void PPB_Graphics3D_Impl::TakeFrontBuffer() {
127   taken_front_buffer_ = GenerateMailbox();
128   command_buffer_->TakeFrontBuffer(taken_front_buffer_);
129 }
130 
ReturnFrontBuffer(const gpu::Mailbox & mailbox,const gpu::SyncToken & sync_token,bool is_lost)131 void PPB_Graphics3D_Impl::ReturnFrontBuffer(const gpu::Mailbox& mailbox,
132                                             const gpu::SyncToken& sync_token,
133                                             bool is_lost) {
134   command_buffer_->ReturnFrontBuffer(mailbox, sync_token, is_lost);
135   mailboxes_to_reuse_.push_back(mailbox);
136 }
137 
BindToInstance(bool bind)138 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
139   bound_to_instance_ = bind;
140   return true;
141 }
142 
IsOpaque()143 bool PPB_Graphics3D_Impl::IsOpaque() { return !has_alpha_; }
144 
ViewInitiatedPaint()145 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
146   commit_pending_ = false;
147 
148   if (HasPendingSwap())
149     SwapBuffersACK(PP_OK);
150 }
151 
GetCommandBufferProxy()152 gpu::CommandBufferProxyImpl* PPB_Graphics3D_Impl::GetCommandBufferProxy() {
153   DCHECK(command_buffer_);
154   return command_buffer_.get();
155 }
156 
GetCommandBuffer()157 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
158   return command_buffer_.get();
159 }
160 
GetGpuControl()161 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
162   return command_buffer_.get();
163 }
164 
DoSwapBuffers(const gpu::SyncToken & sync_token,const gfx::Size & size)165 int32_t PPB_Graphics3D_Impl::DoSwapBuffers(const gpu::SyncToken& sync_token,
166                                            const gfx::Size& size) {
167   DCHECK(command_buffer_);
168   if (taken_front_buffer_.IsZero()) {
169     DLOG(ERROR) << "TakeFrontBuffer should be called before DoSwapBuffers";
170     return PP_ERROR_FAILED;
171   }
172 
173   if (bound_to_instance_) {
174     // If we are bound to the instance, we need to ask the compositor
175     // to commit our backing texture so that the graphics appears on the page.
176     // When the backing texture will be committed we get notified via
177     // ViewFlushedPaint().
178     //
179     // Don't need to check for NULL from GetPluginInstance since when we're
180     // bound, we know our instance is valid.
181     bool is_overlay_candidate = use_image_chromium_;
182     // TODO(reveman): Get texture target from browser process.
183     uint32_t target = GL_TEXTURE_2D;
184 #if defined(OS_MACOSX)
185     if (use_image_chromium_)
186       target = GL_TEXTURE_RECTANGLE_ARB;
187 #endif
188     viz::TransferableResource resource = viz::TransferableResource::MakeGL(
189         taken_front_buffer_, GL_LINEAR, target, sync_token, size,
190         is_overlay_candidate);
191     HostGlobals::Get()
192         ->GetInstance(pp_instance())
193         ->CommitTransferableResource(resource);
194     commit_pending_ = true;
195   } else {
196     // Wait for the command to complete on the GPU to allow for throttling.
197     command_buffer_->SignalSyncToken(
198         sync_token, base::BindOnce(&PPB_Graphics3D_Impl::OnSwapBuffers,
199                                    weak_ptr_factory_.GetWeakPtr()));
200   }
201 
202   return PP_OK_COMPLETIONPENDING;
203 }
204 
InitRaw(PPB_Graphics3D_API * share_context,const gpu::ContextCreationAttribs & requested_attribs,gpu::Capabilities * capabilities,const base::UnsafeSharedMemoryRegion ** shared_state_region,gpu::CommandBufferId * command_buffer_id)205 bool PPB_Graphics3D_Impl::InitRaw(
206     PPB_Graphics3D_API* share_context,
207     const gpu::ContextCreationAttribs& requested_attribs,
208     gpu::Capabilities* capabilities,
209     const base::UnsafeSharedMemoryRegion** shared_state_region,
210     gpu::CommandBufferId* command_buffer_id) {
211   PepperPluginInstanceImpl* plugin_instance =
212       HostGlobals::Get()->GetInstance(pp_instance());
213   if (!plugin_instance)
214     return false;
215 
216   RenderFrame* render_frame = plugin_instance->GetRenderFrame();
217   if (!render_frame)
218     return false;
219 
220   const WebPreferences& prefs = render_frame->GetWebkitPreferences();
221 
222   // 3D access might be disabled.
223   if (!prefs.pepper_3d_enabled)
224     return false;
225 
226   // Force SW rendering for keyframe extraction to avoid pixel reads from VRAM.
227   PluginInstanceThrottlerImpl* throttler = plugin_instance->throttler();
228   if (throttler && throttler->needs_representative_keyframe())
229     return false;
230 
231   RenderThreadImpl* render_thread = RenderThreadImpl::current();
232   if (!render_thread)
233     return false;
234   if (render_thread->IsGpuCompositingDisabled())
235     return false;
236 
237   scoped_refptr<gpu::GpuChannelHost> channel =
238       render_thread->EstablishGpuChannelSync();
239   if (!channel)
240     return false;
241   // 3D access might be blacklisted.
242   if (channel->gpu_feature_info()
243           .status_values[gpu::GPU_FEATURE_TYPE_ACCELERATED_WEBGL] ==
244       gpu::kGpuFeatureStatusBlacklisted) {
245     return false;
246   }
247 
248   has_alpha_ = requested_attribs.alpha_size > 0;
249 
250   gpu::ContextCreationAttribs attrib_helper = requested_attribs;
251   attrib_helper.should_use_native_gmb_for_backbuffer = use_image_chromium_;
252   attrib_helper.context_type = gpu::CONTEXT_TYPE_OPENGLES2;
253 
254   gpu::CommandBufferProxyImpl* share_buffer = nullptr;
255   if (!plugin_instance->is_flash_plugin())
256     UMA_HISTOGRAM_BOOLEAN("Pepper.Graphics3DHasShareGroup", !!share_context);
257   if (share_context) {
258     PPB_Graphics3D_Impl* share_graphics =
259         static_cast<PPB_Graphics3D_Impl*>(share_context);
260     share_buffer = share_graphics->GetCommandBufferProxy();
261   }
262 
263   command_buffer_ = std::make_unique<gpu::CommandBufferProxyImpl>(
264       std::move(channel), render_thread->GetGpuMemoryBufferManager(),
265       kGpuStreamIdDefault, base::ThreadTaskRunnerHandle::Get());
266   auto result = command_buffer_->Initialize(
267       gpu::kNullSurfaceHandle, share_buffer, kGpuStreamPriorityDefault,
268       attrib_helper, GURL::EmptyGURL());
269   if (result != gpu::ContextResult::kSuccess)
270     return false;
271 
272   command_buffer_->SetGpuControlClient(this);
273 
274   if (shared_state_region)
275     *shared_state_region = &command_buffer_->GetSharedStateRegion();
276   if (capabilities)
277     *capabilities = command_buffer_->GetCapabilities();
278   if (command_buffer_id)
279     *command_buffer_id = command_buffer_->GetCommandBufferID();
280 
281   return true;
282 }
283 
OnGpuControlErrorMessage(const char * message,int32_t id)284 void PPB_Graphics3D_Impl::OnGpuControlErrorMessage(const char* message,
285                                                    int32_t id) {
286   if (!bound_to_instance_)
287     return;
288   WebPluginContainer* container =
289       HostGlobals::Get()->GetInstance(pp_instance())->container();
290   if (!container)
291     return;
292   WebLocalFrame* frame = container->GetDocument().GetFrame();
293   if (!frame)
294     return;
295   WebConsoleMessage console_message = WebConsoleMessage(
296       blink::mojom::ConsoleMessageLevel::kError, WebString::FromUTF8(message));
297   frame->AddMessageToConsole(console_message);
298 }
299 
OnGpuControlLostContext()300 void PPB_Graphics3D_Impl::OnGpuControlLostContext() {
301 #if DCHECK_IS_ON()
302   // This should never occur more than once.
303   DCHECK(!lost_context_);
304   lost_context_ = true;
305 #endif
306 
307   // Don't need to check for null from GetPluginInstance since when we're
308   // bound, we know our instance is valid.
309   if (bound_to_instance_) {
310     HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(pp_instance(),
311                                                                  0);
312   }
313 
314   // Send context lost to plugin. This may have been caused by a PPAPI call, so
315   // avoid re-entering.
316   base::ThreadTaskRunnerHandle::Get()->PostTask(
317       FROM_HERE, base::BindOnce(&PPB_Graphics3D_Impl::SendContextLost,
318                                 weak_ptr_factory_.GetWeakPtr()));
319 }
320 
OnGpuControlLostContextMaybeReentrant()321 void PPB_Graphics3D_Impl::OnGpuControlLostContextMaybeReentrant() {
322   // No internal state to update on lost context.
323 }
324 
OnGpuControlSwapBuffersCompleted(const gpu::SwapBuffersCompleteParams & params)325 void PPB_Graphics3D_Impl::OnGpuControlSwapBuffersCompleted(
326     const gpu::SwapBuffersCompleteParams& params) {}
327 
OnGpuControlReturnData(base::span<const uint8_t> data)328 void PPB_Graphics3D_Impl::OnGpuControlReturnData(
329     base::span<const uint8_t> data) {
330   NOTIMPLEMENTED();
331 }
332 
OnSwapBuffers()333 void PPB_Graphics3D_Impl::OnSwapBuffers() {
334   if (HasPendingSwap()) {
335     // If we're off-screen, no need to trigger and wait for compositing.
336     // Just send the swap-buffers ACK to the plugin immediately.
337     commit_pending_ = false;
338     SwapBuffersACK(PP_OK);
339   }
340 }
341 
SendContextLost()342 void PPB_Graphics3D_Impl::SendContextLost() {
343   // By the time we run this, the instance may have been deleted, or in the
344   // process of being deleted. Even in the latter case, we don't want to send a
345   // callback after DidDestroy.
346   PepperPluginInstanceImpl* instance =
347       HostGlobals::Get()->GetInstance(pp_instance());
348   if (!instance || !instance->container())
349     return;
350 
351   // This PPB_Graphics3D_Impl could be deleted during the call to
352   // GetPluginInterface (which sends a sync message in some cases). We still
353   // send the Graphics3DContextLost to the plugin; the instance may care about
354   // that event even though this context has been destroyed.
355   PP_Instance this_pp_instance = pp_instance();
356   const PPP_Graphics3D* ppp_graphics_3d = static_cast<const PPP_Graphics3D*>(
357       instance->module()->GetPluginInterface(PPP_GRAPHICS_3D_INTERFACE));
358   // We have to check *again* that the instance exists, because it could have
359   // been deleted during GetPluginInterface(). Even the PluginModule could be
360   // deleted, but in that case, the instance should also be gone, so the
361   // GetInstance check covers both cases.
362   if (ppp_graphics_3d && HostGlobals::Get()->GetInstance(this_pp_instance))
363     ppp_graphics_3d->Graphics3DContextLost(this_pp_instance);
364 }
365 
GenerateMailbox()366 gpu::Mailbox PPB_Graphics3D_Impl::GenerateMailbox() {
367   if (!mailboxes_to_reuse_.empty()) {
368     gpu::Mailbox mailbox = mailboxes_to_reuse_.back();
369     mailboxes_to_reuse_.pop_back();
370     return mailbox;
371   }
372 
373   return gpu::Mailbox::Generate();
374 }
375 
376 }  // namespace content
377