Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / renderer / pepper / ppb_graphics_3d_impl.cc
blob33087c0737485a983fce9234eefe5927c5230415
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/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;
40 namespace content {
42 namespace {
44 const int32 kCommandBufferSize = 1024 * 1024;
45 const int32 kTransferBufferSize = 1024 * 1024;
47 } // namespace
49 PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance)
50 : PPB_Graphics3D_Shared(instance),
51 bound_to_instance_(false),
52 commit_pending_(false),
53 sync_point_(0),
54 has_alpha_(false),
55 weak_ptr_factory_(this) {}
57 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {}
59 // static
60 PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance,
61 PP_Resource share_context,
62 const int32_t* attrib_list) {
63 PPB_Graphics3D_API* share_api = NULL;
64 if (share_context) {
65 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
66 if (enter.failed())
67 return 0;
68 share_api = enter.object();
70 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
71 new PPB_Graphics3D_Impl(instance));
72 if (!graphics_3d->Init(share_api, attrib_list))
73 return 0;
74 return graphics_3d->GetReference();
77 // static
78 PP_Resource PPB_Graphics3D_Impl::CreateRaw(
79 PP_Instance instance,
80 PP_Resource share_context,
81 const int32_t* attrib_list,
82 gpu::Capabilities* capabilities,
83 base::SharedMemoryHandle* shared_state_handle,
84 uint64_t* command_buffer_id) {
85 PPB_Graphics3D_API* share_api = NULL;
86 if (share_context) {
87 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
88 if (enter.failed())
89 return 0;
90 share_api = enter.object();
92 scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
93 new PPB_Graphics3D_Impl(instance));
94 if (!graphics_3d->InitRaw(share_api, attrib_list, capabilities,
95 shared_state_handle, command_buffer_id))
96 return 0;
97 return graphics_3d->GetReference();
100 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
101 GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
102 return PP_TRUE;
105 scoped_refptr<gpu::Buffer> PPB_Graphics3D_Impl::CreateTransferBuffer(
106 uint32_t size,
107 int32_t* id) {
108 return GetCommandBuffer()->CreateTransferBuffer(size, id);
111 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
112 GetCommandBuffer()->DestroyTransferBuffer(id);
113 return PP_TRUE;
116 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
117 GetCommandBuffer()->Flush(put_offset);
118 return PP_TRUE;
121 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForTokenInRange(
122 int32_t start,
123 int32_t end) {
124 GetCommandBuffer()->WaitForTokenInRange(start, end);
125 return GetCommandBuffer()->GetLastState();
128 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForGetOffsetInRange(
129 int32_t start,
130 int32_t end) {
131 GetCommandBuffer()->WaitForGetOffsetInRange(start, end);
132 return GetCommandBuffer()->GetLastState();
135 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
136 return command_buffer_->InsertSyncPoint();
139 uint32_t PPB_Graphics3D_Impl::InsertFutureSyncPoint() {
140 return command_buffer_->InsertFutureSyncPoint();
143 void PPB_Graphics3D_Impl::RetireSyncPoint(uint32_t sync_point) {
144 return command_buffer_->RetireSyncPoint(sync_point);
147 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
148 bound_to_instance_ = bind;
149 return true;
152 bool PPB_Graphics3D_Impl::IsOpaque() { return !has_alpha_; }
154 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
155 commit_pending_ = false;
157 if (HasPendingSwap())
158 SwapBuffersACK(PP_OK);
161 CommandBufferProxyImpl* PPB_Graphics3D_Impl::GetCommandBufferProxy() {
162 DCHECK(command_buffer_);
163 return command_buffer_.get();
166 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
167 return command_buffer_.get();
170 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
171 return command_buffer_.get();
174 int32 PPB_Graphics3D_Impl::DoSwapBuffers() {
175 DCHECK(command_buffer_);
176 // We do not have a GLES2 implementation when using an OOP proxy.
177 // The plugin-side proxy is responsible for adding the SwapBuffers command
178 // to the command buffer in that case.
179 if (gles2_impl())
180 gles2_impl()->SwapBuffers();
182 // Since the backing texture has been updated, a new sync point should be
183 // inserted.
184 sync_point_ = command_buffer_->InsertSyncPoint();
186 if (bound_to_instance_) {
187 // If we are bound to the instance, we need to ask the compositor
188 // to commit our backing texture so that the graphics appears on the page.
189 // When the backing texture will be committed we get notified via
190 // ViewFlushedPaint().
192 // Don't need to check for NULL from GetPluginInstance since when we're
193 // bound, we know our instance is valid.
194 HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture();
195 commit_pending_ = true;
196 } else {
197 // Wait for the command to complete on the GPU to allow for throttling.
198 command_buffer_->SignalSyncPoint(
199 sync_point_,
200 base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers,
201 weak_ptr_factory_.GetWeakPtr()));
204 return PP_OK_COMPLETIONPENDING;
207 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context,
208 const int32_t* attrib_list) {
209 if (!InitRaw(share_context, attrib_list, NULL, NULL, NULL))
210 return false;
212 gpu::gles2::GLES2Implementation* share_gles2 = NULL;
213 if (share_context) {
214 share_gles2 =
215 static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl();
218 return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize, share_gles2);
221 bool PPB_Graphics3D_Impl::InitRaw(
222 PPB_Graphics3D_API* share_context,
223 const int32_t* attrib_list,
224 gpu::Capabilities* capabilities,
225 base::SharedMemoryHandle* shared_state_handle,
226 uint64_t* command_buffer_id) {
227 PepperPluginInstanceImpl* plugin_instance =
228 HostGlobals::Get()->GetInstance(pp_instance());
229 if (!plugin_instance)
230 return false;
232 const WebPreferences& prefs =
233 static_cast<RenderViewImpl*>(plugin_instance->GetRenderView())
234 ->webkit_preferences();
235 // 3D access might be disabled or blacklisted.
236 if (!prefs.pepper_3d_enabled)
237 return false;
239 // Force SW rendering for keyframe extraction to avoid pixel reads from VRAM.
240 PluginInstanceThrottlerImpl* throttler = plugin_instance->throttler();
241 if (throttler && throttler->needs_representative_keyframe())
242 return false;
244 RenderThreadImpl* render_thread = RenderThreadImpl::current();
245 if (!render_thread)
246 return false;
248 channel_ = render_thread->EstablishGpuChannelSync(
249 CAUSE_FOR_GPU_LAUNCH_PEPPERPLATFORMCONTEXT3DIMPL_INITIALIZE);
250 if (!channel_.get())
251 return false;
253 gfx::Size surface_size;
254 std::vector<int32> attribs;
255 gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
256 // TODO(alokp): Change GpuChannelHost::CreateOffscreenCommandBuffer()
257 // interface to accept width and height in the attrib_list so that
258 // we do not need to filter for width and height here.
259 if (attrib_list) {
260 for (const int32_t* attr = attrib_list; attr[0] != PP_GRAPHICS3DATTRIB_NONE;
261 attr += 2) {
262 switch (attr[0]) {
263 case PP_GRAPHICS3DATTRIB_WIDTH:
264 surface_size.set_width(attr[1]);
265 break;
266 case PP_GRAPHICS3DATTRIB_HEIGHT:
267 surface_size.set_height(attr[1]);
268 break;
269 case PP_GRAPHICS3DATTRIB_GPU_PREFERENCE:
270 gpu_preference =
271 (attr[1] == PP_GRAPHICS3DATTRIB_GPU_PREFERENCE_LOW_POWER)
272 ? gfx::PreferIntegratedGpu
273 : gfx::PreferDiscreteGpu;
274 break;
275 case PP_GRAPHICS3DATTRIB_ALPHA_SIZE:
276 has_alpha_ = attr[1] > 0;
277 // fall-through
278 default:
279 attribs.push_back(attr[0]);
280 attribs.push_back(attr[1]);
281 break;
284 attribs.push_back(PP_GRAPHICS3DATTRIB_NONE);
287 CommandBufferProxyImpl* share_buffer = NULL;
288 if (share_context) {
289 PPB_Graphics3D_Impl* share_graphics =
290 static_cast<PPB_Graphics3D_Impl*>(share_context);
291 share_buffer = share_graphics->GetCommandBufferProxy();
294 command_buffer_ = channel_->CreateOffscreenCommandBuffer(
295 surface_size, share_buffer, GpuChannelHost::kDefaultStreamId,
296 GpuChannelHost::kDefaultStreamPriority, attribs, GURL::EmptyGURL(),
297 gpu_preference);
298 if (!command_buffer_)
299 return false;
300 if (!command_buffer_->Initialize())
301 return false;
302 if (shared_state_handle)
303 *shared_state_handle = command_buffer_->GetSharedStateHandle();
304 if (capabilities)
305 *capabilities = command_buffer_->GetCapabilities();
306 if (command_buffer_id)
307 *command_buffer_id = command_buffer_->GetCommandBufferID();
308 mailbox_ = gpu::Mailbox::Generate();
309 if (!command_buffer_->ProduceFrontBuffer(mailbox_))
310 return false;
311 sync_point_ = command_buffer_->InsertSyncPoint();
313 command_buffer_->SetContextLostCallback(base::Bind(
314 &PPB_Graphics3D_Impl::OnContextLost, weak_ptr_factory_.GetWeakPtr()));
316 command_buffer_->SetOnConsoleMessageCallback(base::Bind(
317 &PPB_Graphics3D_Impl::OnConsoleMessage, weak_ptr_factory_.GetWeakPtr()));
318 return true;
321 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message, int id) {
322 if (!bound_to_instance_)
323 return;
324 WebPluginContainer* container =
325 HostGlobals::Get()->GetInstance(pp_instance())->container();
326 if (!container)
327 return;
328 WebLocalFrame* frame = container->element().document().frame();
329 if (!frame)
330 return;
331 WebConsoleMessage console_message = WebConsoleMessage(
332 WebConsoleMessage::LevelError, WebString(base::UTF8ToUTF16(message)));
333 frame->addMessageToConsole(console_message);
336 void PPB_Graphics3D_Impl::OnSwapBuffers() {
337 if (HasPendingSwap()) {
338 // If we're off-screen, no need to trigger and wait for compositing.
339 // Just send the swap-buffers ACK to the plugin immediately.
340 commit_pending_ = false;
341 SwapBuffersACK(PP_OK);
345 void PPB_Graphics3D_Impl::OnContextLost() {
346 // Don't need to check for NULL from GetPluginInstance since when we're
347 // bound, we know our instance is valid.
348 if (bound_to_instance_) {
349 HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(pp_instance(),
353 // Send context lost to plugin. This may have been caused by a PPAPI call, so
354 // avoid re-entering.
355 base::ThreadTaskRunnerHandle::Get()->PostTask(
356 FROM_HERE, base::Bind(&PPB_Graphics3D_Impl::SendContextLost,
357 weak_ptr_factory_.GetWeakPtr()));
360 void PPB_Graphics3D_Impl::SendContextLost() {
361 // By the time we run this, the instance may have been deleted, or in the
362 // process of being deleted. Even in the latter case, we don't want to send a
363 // callback after DidDestroy.
364 PepperPluginInstanceImpl* instance =
365 HostGlobals::Get()->GetInstance(pp_instance());
366 if (!instance || !instance->container())
367 return;
369 // This PPB_Graphics3D_Impl could be deleted during the call to
370 // GetPluginInterface (which sends a sync message in some cases). We still
371 // send the Graphics3DContextLost to the plugin; the instance may care about
372 // that event even though this context has been destroyed.
373 PP_Instance this_pp_instance = pp_instance();
374 const PPP_Graphics3D* ppp_graphics_3d = static_cast<const PPP_Graphics3D*>(
375 instance->module()->GetPluginInterface(PPP_GRAPHICS_3D_INTERFACE));
376 // We have to check *again* that the instance exists, because it could have
377 // been deleted during GetPluginInterface(). Even the PluginModule could be
378 // deleted, but in that case, the instance should also be gone, so the
379 // GetInstance check covers both cases.
380 if (ppp_graphics_3d && HostGlobals::Get()->GetInstance(this_pp_instance))
381 ppp_graphics_3d->Graphics3DContextLost(this_pp_instance);
384 } // namespace content