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.
5 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
8 #include "base/command_line.h"
9 #include "base/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "content/common/gpu/client/command_buffer_proxy_impl.h"
14 #include "content/common/gpu/client/gpu_channel_host.h"
15 #include "content/public/common/content_switches.h"
16 #include "content/public/common/web_preferences.h"
17 #include "content/renderer/pepper/host_globals.h"
18 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
19 #include "content/renderer/pepper/plugin_instance_throttler_impl.h"
20 #include "content/renderer/pepper/plugin_module.h"
21 #include "content/renderer/render_thread_impl.h"
22 #include "content/renderer/render_view_impl.h"
23 #include "gpu/command_buffer/client/gles2_implementation.h"
24 #include "ppapi/c/ppp_graphics_3d.h"
25 #include "ppapi/thunk/enter.h"
26 #include "third_party/WebKit/public/platform/WebString.h"
27 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
28 #include "third_party/WebKit/public/web/WebDocument.h"
29 #include "third_party/WebKit/public/web/WebElement.h"
30 #include "third_party/WebKit/public/web/WebLocalFrame.h"
31 #include "third_party/WebKit/public/web/WebPluginContainer.h"
33 using ppapi::thunk::EnterResourceNoLock
;
34 using ppapi::thunk::PPB_Graphics3D_API
;
35 using blink::WebConsoleMessage
;
36 using blink::WebLocalFrame
;
37 using blink::WebPluginContainer
;
38 using blink::WebString
;
44 const int32 kCommandBufferSize
= 1024 * 1024;
45 const int32 kTransferBufferSize
= 1024 * 1024;
49 PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance
)
50 : PPB_Graphics3D_Shared(instance
),
51 bound_to_instance_(false),
52 commit_pending_(false),
55 command_buffer_(NULL
),
56 weak_ptr_factory_(this) {}
58 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
60 if (command_buffer_
) {
61 DCHECK(channel_
.get());
62 channel_
->DestroyCommandBuffer(command_buffer_
);
63 command_buffer_
= NULL
;
70 PP_Resource
PPB_Graphics3D_Impl::Create(PP_Instance instance
,
71 PP_Resource share_context
,
72 const int32_t* attrib_list
) {
73 PPB_Graphics3D_API
* share_api
= NULL
;
75 EnterResourceNoLock
<PPB_Graphics3D_API
> enter(share_context
, true);
78 share_api
= enter
.object();
80 scoped_refptr
<PPB_Graphics3D_Impl
> graphics_3d(
81 new PPB_Graphics3D_Impl(instance
));
82 if (!graphics_3d
->Init(share_api
, attrib_list
))
84 return graphics_3d
->GetReference();
88 PP_Resource
PPB_Graphics3D_Impl::CreateRaw(
90 PP_Resource share_context
,
91 const int32_t* attrib_list
,
92 gpu::Capabilities
* capabilities
,
93 base::SharedMemoryHandle
* shared_state_handle
) {
94 PPB_Graphics3D_API
* share_api
= NULL
;
96 EnterResourceNoLock
<PPB_Graphics3D_API
> enter(share_context
, true);
99 share_api
= enter
.object();
101 scoped_refptr
<PPB_Graphics3D_Impl
> graphics_3d(
102 new PPB_Graphics3D_Impl(instance
));
103 if (!graphics_3d
->InitRaw(share_api
, attrib_list
, capabilities
,
104 shared_state_handle
))
106 return graphics_3d
->GetReference();
109 PP_Bool
PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id
) {
110 GetCommandBuffer()->SetGetBuffer(transfer_buffer_id
);
114 scoped_refptr
<gpu::Buffer
> PPB_Graphics3D_Impl::CreateTransferBuffer(
117 return GetCommandBuffer()->CreateTransferBuffer(size
, id
);
120 PP_Bool
PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id
) {
121 GetCommandBuffer()->DestroyTransferBuffer(id
);
125 PP_Bool
PPB_Graphics3D_Impl::Flush(int32_t put_offset
) {
126 GetCommandBuffer()->Flush(put_offset
);
130 gpu::CommandBuffer::State
PPB_Graphics3D_Impl::WaitForTokenInRange(
133 GetCommandBuffer()->WaitForTokenInRange(start
, end
);
134 return GetCommandBuffer()->GetLastState();
137 gpu::CommandBuffer::State
PPB_Graphics3D_Impl::WaitForGetOffsetInRange(
140 GetCommandBuffer()->WaitForGetOffsetInRange(start
, end
);
141 return GetCommandBuffer()->GetLastState();
144 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
145 return command_buffer_
->InsertSyncPoint();
148 uint32_t PPB_Graphics3D_Impl::InsertFutureSyncPoint() {
149 return command_buffer_
->InsertFutureSyncPoint();
152 void PPB_Graphics3D_Impl::RetireSyncPoint(uint32_t sync_point
) {
153 return command_buffer_
->RetireSyncPoint(sync_point
);
156 bool PPB_Graphics3D_Impl::BindToInstance(bool bind
) {
157 bound_to_instance_
= bind
;
161 bool PPB_Graphics3D_Impl::IsOpaque() { return !has_alpha_
; }
163 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
164 commit_pending_
= false;
166 if (HasPendingSwap())
167 SwapBuffersACK(PP_OK
);
170 int PPB_Graphics3D_Impl::GetCommandBufferRouteId() {
171 DCHECK(command_buffer_
);
172 return command_buffer_
->GetRouteID();
175 gpu::CommandBuffer
* PPB_Graphics3D_Impl::GetCommandBuffer() {
176 return command_buffer_
;
179 gpu::GpuControl
* PPB_Graphics3D_Impl::GetGpuControl() {
180 return command_buffer_
;
183 int32
PPB_Graphics3D_Impl::DoSwapBuffers() {
184 DCHECK(command_buffer_
);
185 // We do not have a GLES2 implementation when using an OOP proxy.
186 // The plugin-side proxy is responsible for adding the SwapBuffers command
187 // to the command buffer in that case.
189 gles2_impl()->SwapBuffers();
191 // Since the backing texture has been updated, a new sync point should be
193 sync_point_
= command_buffer_
->InsertSyncPoint();
195 if (bound_to_instance_
) {
196 // If we are bound to the instance, we need to ask the compositor
197 // to commit our backing texture so that the graphics appears on the page.
198 // When the backing texture will be committed we get notified via
199 // ViewFlushedPaint().
201 // Don't need to check for NULL from GetPluginInstance since when we're
202 // bound, we know our instance is valid.
203 HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture();
204 commit_pending_
= true;
206 // Wait for the command to complete on the GPU to allow for throttling.
207 command_buffer_
->SignalSyncPoint(
209 base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers
,
210 weak_ptr_factory_
.GetWeakPtr()));
213 return PP_OK_COMPLETIONPENDING
;
216 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API
* share_context
,
217 const int32_t* attrib_list
) {
218 if (!InitRaw(share_context
, attrib_list
, NULL
, NULL
))
221 gpu::gles2::GLES2Implementation
* share_gles2
= NULL
;
224 static_cast<PPB_Graphics3D_Shared
*>(share_context
)->gles2_impl();
227 return CreateGLES2Impl(kCommandBufferSize
, kTransferBufferSize
, share_gles2
);
230 bool PPB_Graphics3D_Impl::InitRaw(
231 PPB_Graphics3D_API
* share_context
,
232 const int32_t* attrib_list
,
233 gpu::Capabilities
* capabilities
,
234 base::SharedMemoryHandle
* shared_state_handle
) {
235 PepperPluginInstanceImpl
* plugin_instance
=
236 HostGlobals::Get()->GetInstance(pp_instance());
237 if (!plugin_instance
)
240 const WebPreferences
& prefs
=
241 static_cast<RenderViewImpl
*>(plugin_instance
->GetRenderView())
242 ->webkit_preferences();
243 // 3D access might be disabled or blacklisted.
244 if (!prefs
.pepper_3d_enabled
)
247 // Force SW rendering for keyframe extraction to avoid pixel reads from VRAM.
248 PluginInstanceThrottlerImpl
* throttler
= plugin_instance
->throttler();
249 if (throttler
&& throttler
->needs_representative_keyframe())
252 RenderThreadImpl
* render_thread
= RenderThreadImpl::current();
256 channel_
= render_thread
->EstablishGpuChannelSync(
257 CAUSE_FOR_GPU_LAUNCH_PEPPERPLATFORMCONTEXT3DIMPL_INITIALIZE
);
261 gfx::Size surface_size
;
262 std::vector
<int32
> attribs
;
263 gfx::GpuPreference gpu_preference
= gfx::PreferDiscreteGpu
;
264 // TODO(alokp): Change GpuChannelHost::CreateOffscreenCommandBuffer()
265 // interface to accept width and height in the attrib_list so that
266 // we do not need to filter for width and height here.
268 for (const int32_t* attr
= attrib_list
; attr
[0] != PP_GRAPHICS3DATTRIB_NONE
;
271 case PP_GRAPHICS3DATTRIB_WIDTH
:
272 surface_size
.set_width(attr
[1]);
274 case PP_GRAPHICS3DATTRIB_HEIGHT
:
275 surface_size
.set_height(attr
[1]);
277 case PP_GRAPHICS3DATTRIB_GPU_PREFERENCE
:
279 (attr
[1] == PP_GRAPHICS3DATTRIB_GPU_PREFERENCE_LOW_POWER
)
280 ? gfx::PreferIntegratedGpu
281 : gfx::PreferDiscreteGpu
;
283 case PP_GRAPHICS3DATTRIB_ALPHA_SIZE
:
284 has_alpha_
= attr
[1] > 0;
287 attribs
.push_back(attr
[0]);
288 attribs
.push_back(attr
[1]);
292 attribs
.push_back(PP_GRAPHICS3DATTRIB_NONE
);
295 CommandBufferProxyImpl
* share_buffer
= NULL
;
297 PPB_Graphics3D_Impl
* share_graphics
=
298 static_cast<PPB_Graphics3D_Impl
*>(share_context
);
299 share_buffer
= share_graphics
->command_buffer_
;
302 command_buffer_
= channel_
->CreateOffscreenCommandBuffer(
303 surface_size
, share_buffer
, attribs
, GURL::EmptyGURL(), gpu_preference
);
304 if (!command_buffer_
)
306 if (!command_buffer_
->Initialize())
308 if (shared_state_handle
)
309 *shared_state_handle
= command_buffer_
->GetSharedStateHandle();
311 *capabilities
= command_buffer_
->GetCapabilities();
312 mailbox_
= gpu::Mailbox::Generate();
313 if (!command_buffer_
->ProduceFrontBuffer(mailbox_
))
315 sync_point_
= command_buffer_
->InsertSyncPoint();
317 command_buffer_
->SetContextLostCallback(base::Bind(
318 &PPB_Graphics3D_Impl::OnContextLost
, weak_ptr_factory_
.GetWeakPtr()));
320 command_buffer_
->SetOnConsoleMessageCallback(base::Bind(
321 &PPB_Graphics3D_Impl::OnConsoleMessage
, weak_ptr_factory_
.GetWeakPtr()));
325 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string
& message
, int id
) {
326 if (!bound_to_instance_
)
328 WebPluginContainer
* container
=
329 HostGlobals::Get()->GetInstance(pp_instance())->container();
332 WebLocalFrame
* frame
= container
->element().document().frame();
335 WebConsoleMessage console_message
= WebConsoleMessage(
336 WebConsoleMessage::LevelError
, WebString(base::UTF8ToUTF16(message
)));
337 frame
->addMessageToConsole(console_message
);
340 void PPB_Graphics3D_Impl::OnSwapBuffers() {
341 if (HasPendingSwap()) {
342 // If we're off-screen, no need to trigger and wait for compositing.
343 // Just send the swap-buffers ACK to the plugin immediately.
344 commit_pending_
= false;
345 SwapBuffersACK(PP_OK
);
349 void PPB_Graphics3D_Impl::OnContextLost() {
350 // Don't need to check for NULL from GetPluginInstance since when we're
351 // bound, we know our instance is valid.
352 if (bound_to_instance_
) {
353 HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(pp_instance(),
357 // Send context lost to plugin. This may have been caused by a PPAPI call, so
358 // avoid re-entering.
359 base::ThreadTaskRunnerHandle::Get()->PostTask(
360 FROM_HERE
, base::Bind(&PPB_Graphics3D_Impl::SendContextLost
,
361 weak_ptr_factory_
.GetWeakPtr()));
364 void PPB_Graphics3D_Impl::SendContextLost() {
365 // By the time we run this, the instance may have been deleted, or in the
366 // process of being deleted. Even in the latter case, we don't want to send a
367 // callback after DidDestroy.
368 PepperPluginInstanceImpl
* instance
=
369 HostGlobals::Get()->GetInstance(pp_instance());
370 if (!instance
|| !instance
->container())
373 // This PPB_Graphics3D_Impl could be deleted during the call to
374 // GetPluginInterface (which sends a sync message in some cases). We still
375 // send the Graphics3DContextLost to the plugin; the instance may care about
376 // that event even though this context has been destroyed.
377 PP_Instance this_pp_instance
= pp_instance();
378 const PPP_Graphics3D
* ppp_graphics_3d
= static_cast<const PPP_Graphics3D
*>(
379 instance
->module()->GetPluginInterface(PPP_GRAPHICS_3D_INTERFACE
));
380 // We have to check *again* that the instance exists, because it could have
381 // been deleted during GetPluginInterface(). Even the PluginModule could be
382 // deleted, but in that case, the instance should also be gone, so the
383 // GetInstance check covers both cases.
384 if (ppp_graphics_3d
&& HostGlobals::Get()->GetInstance(this_pp_instance
))
385 ppp_graphics_3d
->Graphics3DContextLost(this_pp_instance
);
388 } // namespace content