Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / pepper / ppb_graphics_3d_impl.cc
blobb703cef31d160d102429cfff385896f30514e09c
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"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/common/gpu/client/command_buffer_proxy_impl.h"
12 #include "content/common/gpu/client/gpu_channel_host.h"
13 #include "content/public/common/content_switches.h"
14 #include "content/public/common/web_preferences.h"
15 #include "content/renderer/pepper/host_globals.h"
16 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
17 #include "content/renderer/pepper/plugin_instance_throttler_impl.h"
18 #include "content/renderer/pepper/plugin_module.h"
19 #include "content/renderer/render_thread_impl.h"
20 #include "content/renderer/render_view_impl.h"
21 #include "gpu/command_buffer/client/gles2_implementation.h"
22 #include "ppapi/c/ppp_graphics_3d.h"
23 #include "ppapi/thunk/enter.h"
24 #include "third_party/WebKit/public/platform/WebString.h"
25 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
26 #include "third_party/WebKit/public/web/WebDocument.h"
27 #include "third_party/WebKit/public/web/WebElement.h"
28 #include "third_party/WebKit/public/web/WebLocalFrame.h"
29 #include "third_party/WebKit/public/web/WebPluginContainer.h"
31 using ppapi::thunk::EnterResourceNoLock;
32 using ppapi::thunk::PPB_Graphics3D_API;
33 using blink::WebConsoleMessage;
34 using blink::WebLocalFrame;
35 using blink::WebPluginContainer;
36 using blink::WebString;
38 namespace content {
40 namespace {
42 const int32 kCommandBufferSize = 1024 * 1024;
43 const int32 kTransferBufferSize = 1024 * 1024;
45 } // namespace
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 sync_point_(0),
52 has_alpha_(false),
53 command_buffer_(NULL),
54 weak_ptr_factory_(this) {}
56 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
57 DestroyGLES2Impl();
58 if (command_buffer_) {
59 DCHECK(channel_.get());
60 channel_->DestroyCommandBuffer(command_buffer_);
61 command_buffer_ = NULL;
64 channel_ = NULL;
67 // static
68 PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance,
69 PP_Resource share_context,
70 const int32_t* attrib_list) {
71 PPB_Graphics3D_API* share_api = NULL;
72 if (share_context) {
73 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
74 if (enter.failed())
75 return 0;
76 share_api = enter.object();
78 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
79 new PPB_Graphics3D_Impl(instance));
80 if (!graphics_3d->Init(share_api, attrib_list))
81 return 0;
82 return graphics_3d->GetReference();
85 // static
86 PP_Resource PPB_Graphics3D_Impl::CreateRaw(
87 PP_Instance instance,
88 PP_Resource share_context,
89 const int32_t* attrib_list,
90 gpu::Capabilities* capabilities,
91 base::SharedMemoryHandle* shared_state_handle) {
92 PPB_Graphics3D_API* share_api = NULL;
93 if (share_context) {
94 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
95 if (enter.failed())
96 return 0;
97 share_api = enter.object();
99 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
100 new PPB_Graphics3D_Impl(instance));
101 if (!graphics_3d->InitRaw(share_api, attrib_list, capabilities,
102 shared_state_handle))
103 return 0;
104 return graphics_3d->GetReference();
107 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
108 GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
109 return PP_TRUE;
112 scoped_refptr<gpu::Buffer> PPB_Graphics3D_Impl::CreateTransferBuffer(
113 uint32_t size,
114 int32_t* id) {
115 return GetCommandBuffer()->CreateTransferBuffer(size, id);
118 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
119 GetCommandBuffer()->DestroyTransferBuffer(id);
120 return PP_TRUE;
123 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
124 GetCommandBuffer()->Flush(put_offset);
125 return PP_TRUE;
128 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForTokenInRange(
129 int32_t start,
130 int32_t end) {
131 GetCommandBuffer()->WaitForTokenInRange(start, end);
132 return GetCommandBuffer()->GetLastState();
135 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForGetOffsetInRange(
136 int32_t start,
137 int32_t end) {
138 GetCommandBuffer()->WaitForGetOffsetInRange(start, end);
139 return GetCommandBuffer()->GetLastState();
142 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
143 return command_buffer_->InsertSyncPoint();
146 uint32_t PPB_Graphics3D_Impl::InsertFutureSyncPoint() {
147 return command_buffer_->InsertFutureSyncPoint();
150 void PPB_Graphics3D_Impl::RetireSyncPoint(uint32_t sync_point) {
151 return command_buffer_->RetireSyncPoint(sync_point);
154 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
155 bound_to_instance_ = bind;
156 return true;
159 bool PPB_Graphics3D_Impl::IsOpaque() { return !has_alpha_; }
161 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
162 commit_pending_ = false;
164 if (HasPendingSwap())
165 SwapBuffersACK(PP_OK);
168 int PPB_Graphics3D_Impl::GetCommandBufferRouteId() {
169 DCHECK(command_buffer_);
170 return command_buffer_->GetRouteID();
173 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
174 return command_buffer_;
177 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
178 return command_buffer_;
181 int32 PPB_Graphics3D_Impl::DoSwapBuffers() {
182 DCHECK(command_buffer_);
183 // We do not have a GLES2 implementation when using an OOP proxy.
184 // The plugin-side proxy is responsible for adding the SwapBuffers command
185 // to the command buffer in that case.
186 if (gles2_impl())
187 gles2_impl()->SwapBuffers();
189 // Since the backing texture has been updated, a new sync point should be
190 // inserted.
191 sync_point_ = command_buffer_->InsertSyncPoint();
193 if (bound_to_instance_) {
194 // If we are bound to the instance, we need to ask the compositor
195 // to commit our backing texture so that the graphics appears on the page.
196 // When the backing texture will be committed we get notified via
197 // ViewFlushedPaint().
199 // Don't need to check for NULL from GetPluginInstance since when we're
200 // bound, we know our instance is valid.
201 HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture();
202 commit_pending_ = true;
203 } else {
204 // Wait for the command to complete on the GPU to allow for throttling.
205 command_buffer_->SignalSyncPoint(
206 sync_point_,
207 base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers,
208 weak_ptr_factory_.GetWeakPtr()));
211 return PP_OK_COMPLETIONPENDING;
214 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context,
215 const int32_t* attrib_list) {
216 if (!InitRaw(share_context, attrib_list, NULL, NULL))
217 return false;
219 gpu::gles2::GLES2Implementation* share_gles2 = NULL;
220 if (share_context) {
221 share_gles2 =
222 static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl();
225 return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize, share_gles2);
228 bool PPB_Graphics3D_Impl::InitRaw(
229 PPB_Graphics3D_API* share_context,
230 const int32_t* attrib_list,
231 gpu::Capabilities* capabilities,
232 base::SharedMemoryHandle* shared_state_handle) {
233 PepperPluginInstanceImpl* plugin_instance =
234 HostGlobals::Get()->GetInstance(pp_instance());
235 if (!plugin_instance)
236 return false;
238 const WebPreferences& prefs =
239 static_cast<RenderViewImpl*>(plugin_instance->GetRenderView())
240 ->webkit_preferences();
241 // 3D access might be disabled or blacklisted.
242 if (!prefs.pepper_3d_enabled)
243 return false;
245 // Force SW rendering for keyframe extraction to avoid pixel reads from VRAM.
246 PluginInstanceThrottlerImpl* throttler = plugin_instance->throttler();
247 if (throttler && throttler->needs_representative_keyframe())
248 return false;
250 RenderThreadImpl* render_thread = RenderThreadImpl::current();
251 if (!render_thread)
252 return false;
254 channel_ = render_thread->EstablishGpuChannelSync(
255 CAUSE_FOR_GPU_LAUNCH_PEPPERPLATFORMCONTEXT3DIMPL_INITIALIZE);
256 if (!channel_.get())
257 return false;
259 gfx::Size surface_size;
260 std::vector<int32> attribs;
261 gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
262 // TODO(alokp): Change GpuChannelHost::CreateOffscreenCommandBuffer()
263 // interface to accept width and height in the attrib_list so that
264 // we do not need to filter for width and height here.
265 if (attrib_list) {
266 for (const int32_t* attr = attrib_list; attr[0] != PP_GRAPHICS3DATTRIB_NONE;
267 attr += 2) {
268 switch (attr[0]) {
269 case PP_GRAPHICS3DATTRIB_WIDTH:
270 surface_size.set_width(attr[1]);
271 break;
272 case PP_GRAPHICS3DATTRIB_HEIGHT:
273 surface_size.set_height(attr[1]);
274 break;
275 case PP_GRAPHICS3DATTRIB_GPU_PREFERENCE:
276 gpu_preference =
277 (attr[1] == PP_GRAPHICS3DATTRIB_GPU_PREFERENCE_LOW_POWER)
278 ? gfx::PreferIntegratedGpu
279 : gfx::PreferDiscreteGpu;
280 break;
281 case PP_GRAPHICS3DATTRIB_ALPHA_SIZE:
282 has_alpha_ = attr[1] > 0;
283 // fall-through
284 default:
285 attribs.push_back(attr[0]);
286 attribs.push_back(attr[1]);
287 break;
290 attribs.push_back(PP_GRAPHICS3DATTRIB_NONE);
293 CommandBufferProxyImpl* share_buffer = NULL;
294 if (share_context) {
295 PPB_Graphics3D_Impl* share_graphics =
296 static_cast<PPB_Graphics3D_Impl*>(share_context);
297 share_buffer = share_graphics->command_buffer_;
300 command_buffer_ = channel_->CreateOffscreenCommandBuffer(
301 surface_size, share_buffer, attribs, GURL::EmptyGURL(), gpu_preference);
302 if (!command_buffer_)
303 return false;
304 if (!command_buffer_->Initialize())
305 return false;
306 if (shared_state_handle)
307 *shared_state_handle = command_buffer_->GetSharedStateHandle();
308 if (capabilities)
309 *capabilities = command_buffer_->GetCapabilities();
310 mailbox_ = gpu::Mailbox::Generate();
311 if (!command_buffer_->ProduceFrontBuffer(mailbox_))
312 return false;
313 sync_point_ = command_buffer_->InsertSyncPoint();
315 command_buffer_->SetChannelErrorCallback(base::Bind(
316 &PPB_Graphics3D_Impl::OnContextLost, weak_ptr_factory_.GetWeakPtr()));
318 command_buffer_->SetOnConsoleMessageCallback(base::Bind(
319 &PPB_Graphics3D_Impl::OnConsoleMessage, weak_ptr_factory_.GetWeakPtr()));
320 return true;
323 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message, int id) {
324 if (!bound_to_instance_)
325 return;
326 WebPluginContainer* container =
327 HostGlobals::Get()->GetInstance(pp_instance())->container();
328 if (!container)
329 return;
330 WebLocalFrame* frame = container->element().document().frame();
331 if (!frame)
332 return;
333 WebConsoleMessage console_message = WebConsoleMessage(
334 WebConsoleMessage::LevelError, WebString(base::UTF8ToUTF16(message)));
335 frame->addMessageToConsole(console_message);
338 void PPB_Graphics3D_Impl::OnSwapBuffers() {
339 if (HasPendingSwap()) {
340 // If we're off-screen, no need to trigger and wait for compositing.
341 // Just send the swap-buffers ACK to the plugin immediately.
342 commit_pending_ = false;
343 SwapBuffersACK(PP_OK);
347 void PPB_Graphics3D_Impl::OnContextLost() {
348 // Don't need to check for NULL from GetPluginInstance since when we're
349 // bound, we know our instance is valid.
350 if (bound_to_instance_) {
351 HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(pp_instance(),
355 // Send context lost to plugin. This may have been caused by a PPAPI call, so
356 // avoid re-entering.
357 base::MessageLoop::current()->PostTask(
358 FROM_HERE,
359 base::Bind(&PPB_Graphics3D_Impl::SendContextLost,
360 weak_ptr_factory_.GetWeakPtr()));
363 void PPB_Graphics3D_Impl::SendContextLost() {
364 // By the time we run this, the instance may have been deleted, or in the
365 // process of being deleted. Even in the latter case, we don't want to send a
366 // callback after DidDestroy.
367 PepperPluginInstanceImpl* instance =
368 HostGlobals::Get()->GetInstance(pp_instance());
369 if (!instance || !instance->container())
370 return;
372 // This PPB_Graphics3D_Impl could be deleted during the call to
373 // GetPluginInterface (which sends a sync message in some cases). We still
374 // send the Graphics3DContextLost to the plugin; the instance may care about
375 // that event even though this context has been destroyed.
376 PP_Instance this_pp_instance = pp_instance();
377 const PPP_Graphics3D* ppp_graphics_3d = static_cast<const PPP_Graphics3D*>(
378 instance->module()->GetPluginInterface(PPP_GRAPHICS_3D_INTERFACE));
379 // We have to check *again* that the instance exists, because it could have
380 // been deleted during GetPluginInterface(). Even the PluginModule could be
381 // deleted, but in that case, the instance should also be gone, so the
382 // GetInstance check covers both cases.
383 if (ppp_graphics_3d && HostGlobals::Get()->GetInstance(this_pp_instance))
384 ppp_graphics_3d->Graphics3DContextLost(this_pp_instance);
387 } // namespace content