Index: chrome/renderer/webplugin_delegate_pepper.cc |
=================================================================== |
--- chrome/renderer/webplugin_delegate_pepper.cc (revision 76844) |
+++ chrome/renderer/webplugin_delegate_pepper.cc (working copy) |
@@ -63,6 +63,14 @@ |
#include "ui/gfx/gdi_util.h" |
#endif |
+#if defined(ENABLE_GPU) |
+#include "webkit/plugins/npapi/plugin_constants_win.h" |
+#endif |
+ |
+#if defined(ENABLE_GPU) |
+using gpu::Buffer; |
+#endif |
+ |
using webkit::npapi::WebPlugin; |
using webkit::npapi::WebPluginDelegate; |
using webkit::npapi::WebPluginResourceClient; |
@@ -79,6 +87,15 @@ |
TransportDIB* dib; |
}; |
+struct Device3DImpl { |
+#if defined(ENABLE_GPU) |
+ gpu::CommandBuffer* command_buffer; |
+#endif |
+ bool dynamically_created; |
+}; |
+ |
+const int32 kDefaultCommandBufferSize = 1024 * 1024; |
+ |
} // namespace |
static const float kPointsPerInch = 72.0; |
@@ -180,6 +197,22 @@ |
instance_ = 0; |
} |
+ |
+ // Destroy the nested GPU plugin only after first destroying the underlying |
+ // Pepper plugin. This is so the Pepper plugin does not attempt to issue |
+ // rendering commands after the GPU plugin has stopped processing them and |
+ // responding to them. |
+ if (nested_delegate_) { |
+#if defined(ENABLE_GPU) |
+ if (command_buffer_) { |
+ nested_delegate_->DestroyCommandBuffer(command_buffer_); |
+ command_buffer_ = NULL; |
+ } |
+#endif |
+ |
+ nested_delegate_->PluginDestroyed(); |
+ nested_delegate_ = NULL; |
+ } |
} |
void WebPluginDelegatePepper::UpdateGeometry( |
@@ -204,6 +237,21 @@ |
new_committed.allocPixels(); |
committed_bitmap_ = new_committed; |
+ // Forward the new geometry to the nested plugin instance. |
+ if (nested_delegate_) |
+ nested_delegate_->UpdateGeometry(window_rect, clip_rect); |
+ |
+#if defined(ENABLE_GPU) |
+#if defined(OS_MACOSX) |
+ // Send the new window size to the command buffer service code so it |
+ // can allocate a new backing store. The handle to the new backing |
+ // store is sent back to the browser asynchronously. |
+ if (command_buffer_) { |
+ command_buffer_->SetWindowSize(window_rect_.size()); |
+ } |
+#endif // OS_MACOSX |
+#endif // ENABLE_GPU |
+ |
if (!instance()) |
return; |
@@ -519,7 +567,8 @@ |
} |
// This is a windowless plugin, so set it to have no handle. Defer this |
- // until we know the plugin will use the 2D device. |
+ // until we know the plugin will use the 2D device. If it uses the 3D device |
+ // it will have a window handle. |
plugin_->SetWindow(gfx::kNullPluginWindow); |
scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext(this)); |
@@ -620,6 +669,76 @@ |
NPError WebPluginDelegatePepper::Device3DInitializeContext( |
const NPDeviceContext3DConfig* config, |
NPDeviceContext3D* context) { |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+#if defined(ENABLE_GPU) |
+ // Check to see if the GPU plugin is already initialized and fail if so. |
+ if (nested_delegate_) |
+ return NPERR_GENERIC_ERROR; |
+ |
+ // Create an instance of the GPU plugin that is responsible for 3D |
+ // rendering. |
+ nested_delegate_ = new WebPluginDelegateProxy( |
+ "application/vnd.google.chrome.gpu-plugin", render_view_); |
+ |
+ // TODO(apatrick): should the GPU plugin be attached to plugin_? |
+ if (nested_delegate_->Initialize(GURL(), |
+ std::vector<std::string>(), |
+ std::vector<std::string>(), |
+ plugin_, |
+ false)) { |
+ plugin_->SetAcceptsInputEvents(true); |
+ |
+ // Ensure the window has the correct size before initializing the |
+ // command buffer. |
+ nested_delegate_->UpdateGeometry(window_rect_, clip_rect_); |
+ |
+ // Ask the GPU plugin to create a command buffer and return a proxy. |
+ command_buffer_ = nested_delegate_->CreateCommandBuffer(); |
+ if (command_buffer_) { |
+ // Initialize the proxy command buffer. |
+ if (command_buffer_->Initialize(config->commandBufferSize)) { |
+ // Get the initial command buffer state. |
+ gpu::CommandBuffer::State state = command_buffer_->GetState(); |
+ |
+ // Initialize the 3D context. |
+ context->reserved = NULL; |
+ context->waitForProgress = true; |
+ Buffer ring_buffer = command_buffer_->GetRingBuffer(); |
+ context->commandBuffer = ring_buffer.ptr; |
+ context->commandBufferSize = state.num_entries; |
+ context->repaintCallback = NULL; |
+ Synchronize3DContext(context, state); |
+ |
+ ScheduleHandleRepaint(instance_->npp(), context); |
+ |
+#if defined(OS_MACOSX) |
+ command_buffer_->SetWindowSize(window_rect_.size()); |
+#endif // OS_MACOSX |
+ |
+ // Make sure the nested delegate shows up in the right place |
+ // on the page. |
+ SendNestedDelegateGeometryToBrowser(window_rect_, clip_rect_); |
+ |
+ // Save the implementation information (the CommandBuffer). |
+ Device3DImpl* impl = new Device3DImpl; |
+ impl->command_buffer = command_buffer_; |
+ impl->dynamically_created = false; |
+ context->reserved = impl; |
+ |
+ return NPERR_NO_ERROR; |
+ } |
+ |
+ nested_delegate_->DestroyCommandBuffer(command_buffer_); |
+ command_buffer_ = NULL; |
+ } |
+ } |
+ |
+ nested_delegate_->PluginDestroyed(); |
+ nested_delegate_ = NULL; |
+#endif // ENABLE_GPU |
+ |
return NPERR_GENERIC_ERROR; |
} |
@@ -642,49 +761,212 @@ |
NPDeviceContext3D* context, |
NPDeviceFlushContextCallbackPtr callback, |
void* user_data) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+#if defined(ENABLE_GPU) |
+ gpu::CommandBuffer::State state; |
+ |
+ if (context->waitForProgress) { |
+ if (callback) { |
+ command_buffer_->AsyncFlush( |
+ context->putOffset, |
+ method_factory3d_.NewRunnableMethod( |
+ &WebPluginDelegatePepper::Device3DUpdateState, |
+ id, |
+ context, |
+ callback, |
+ user_data)); |
+ } else { |
+ state = command_buffer_->FlushSync(context->putOffset); |
+ Synchronize3DContext(context, state); |
+ } |
+ } else { |
+ if (callback) { |
+ command_buffer_->AsyncGetState( |
+ method_factory3d_.NewRunnableMethod( |
+ &WebPluginDelegatePepper::Device3DUpdateState, |
+ id, |
+ context, |
+ callback, |
+ user_data)); |
+ } else { |
+ state = command_buffer_->GetState(); |
+ Synchronize3DContext(context, state); |
+ } |
+ } |
+#endif // ENABLE_GPU |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DDestroyContext( |
NPDeviceContext3D* context) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+#if defined(ENABLE_GPU) |
+ // Prevent any async flush callbacks from being invoked after the context |
+ // has been destroyed. |
+ method_factory3d_.RevokeAll(); |
+ |
+ // TODO(apatrick): this will be much simpler when we switch to the new device |
+ // API. There should be no need for the Device3DImpl and the context will |
+ // always be destroyed dynamically. |
+ Device3DImpl* impl = static_cast<Device3DImpl*>(context->reserved); |
+ bool dynamically_created = impl->dynamically_created; |
+ delete impl; |
+ context->reserved = NULL; |
+ if (dynamically_created) { |
+ delete context; |
+ } |
+ |
+ if (nested_delegate_) { |
+ if (command_buffer_) { |
+ nested_delegate_->DestroyCommandBuffer(command_buffer_); |
+ command_buffer_ = NULL; |
+ } |
+ |
+ nested_delegate_->PluginDestroyed(); |
+ nested_delegate_ = NULL; |
+ } |
+#endif // ENABLE_GPU |
+ |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DCreateBuffer( |
NPDeviceContext3D* context, |
size_t size, |
int32* id) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+#if defined(ENABLE_GPU) |
+ *id = command_buffer_->CreateTransferBuffer(size); |
+ if (*id < 0) |
+ return NPERR_GENERIC_ERROR; |
+#endif // ENABLE_GPU |
+ |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DDestroyBuffer( |
NPDeviceContext3D* context, |
int32 id) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+#if defined(ENABLE_GPU) |
+ command_buffer_->DestroyTransferBuffer(id); |
+#endif // ENABLE_GPU |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DMapBuffer( |
NPDeviceContext3D* context, |
int32 id, |
NPDeviceBuffer* np_buffer) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+#if defined(ENABLE_GPU) |
+ Buffer gpu_buffer; |
+ if (id == NP3DCommandBufferId) { |
+ gpu_buffer = command_buffer_->GetRingBuffer(); |
+ } else { |
+ gpu_buffer = command_buffer_->GetTransferBuffer(id); |
+ } |
+ |
+ np_buffer->ptr = gpu_buffer.ptr; |
+ np_buffer->size = gpu_buffer.size; |
+ if (!np_buffer->ptr) |
+ return NPERR_GENERIC_ERROR; |
+#endif // ENABLE_GPU |
+ |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DGetNumConfigs(int32* num_configs) { |
- return NPERR_GENERIC_ERROR; |
+ if (!num_configs) |
+ return NPERR_GENERIC_ERROR; |
+ |
+ *num_configs = 1; |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DGetConfigAttribs( |
int32 config, |
int32* attrib_list) { |
- return NPERR_GENERIC_ERROR; |
+ // Only one config available currently. |
+ if (config != 0) |
+ return NPERR_GENERIC_ERROR; |
+ |
+ if (attrib_list) { |
+ for (int32* attrib_pair = attrib_list; *attrib_pair; attrib_pair += 2) { |
+ switch (attrib_pair[0]) { |
+ case NP3DAttrib_BufferSize: |
+ attrib_pair[1] = 32; |
+ break; |
+ case NP3DAttrib_AlphaSize: |
+ case NP3DAttrib_BlueSize: |
+ case NP3DAttrib_GreenSize: |
+ case NP3DAttrib_RedSize: |
+ attrib_pair[1] = 8; |
+ break; |
+ case NP3DAttrib_DepthSize: |
+ attrib_pair[1] = 24; |
+ break; |
+ case NP3DAttrib_StencilSize: |
+ attrib_pair[1] = 8; |
+ break; |
+ case NP3DAttrib_SurfaceType: |
+ attrib_pair[1] = 0; |
+ break; |
+ default: |
+ return NPERR_GENERIC_ERROR; |
+ } |
+ } |
+ } |
+ |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DCreateContext( |
int32 config, |
const int32* attrib_list, |
NPDeviceContext3D** context) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+ // Only one config available currently. |
+ if (config != 0) |
+ return NPERR_GENERIC_ERROR; |
+ |
+ // For now, just use the old API to initialize the context. |
+ NPDeviceContext3DConfig old_config; |
+ old_config.commandBufferSize = kDefaultCommandBufferSize; |
+ if (attrib_list) { |
+ for (const int32* attrib_pair = attrib_list; *attrib_pair; |
+ attrib_pair += 2) { |
+ switch (attrib_pair[0]) { |
+ case NP3DAttrib_CommandBufferSize: |
+ old_config.commandBufferSize = attrib_pair[1]; |
+ break; |
+ default: |
+ return NPERR_GENERIC_ERROR; |
+ } |
+ } |
+ } |
+ |
+ *context = new NPDeviceContext3D; |
+ Device3DInitializeContext(&old_config, *context); |
+ |
+ // Flag the context as dynamically created by the browser. TODO(apatrick): |
+ // take this out when all contexts are dynamically created. |
+ Device3DImpl* impl = static_cast<Device3DImpl*>((*context)->reserved); |
+ impl->dynamically_created = true; |
+ |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DRegisterCallback( |
@@ -693,7 +975,19 @@ |
int32 callback_type, |
NPDeviceGenericCallbackPtr callback, |
void* callback_data) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+ switch (callback_type) { |
+ case NP3DCallback_Repaint: |
+ context->repaintCallback = reinterpret_cast<NPDeviceContext3DRepaintPtr>( |
+ callback); |
+ break; |
+ default: |
+ return NPERR_GENERIC_ERROR; |
+ } |
+ |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::Device3DSynchronizeContext( |
@@ -704,7 +998,58 @@ |
int32* output_attrib_list, |
NPDeviceSynchronizeContextCallbackPtr callback, |
void* callback_data) { |
- return NPERR_GENERIC_ERROR; |
+ if (!context) |
+ return NPERR_GENERIC_ERROR; |
+ |
+ // Copy input attributes into context. |
+ if (input_attrib_list) { |
+ for (const int32* attrib_pair = input_attrib_list; |
+ *attrib_pair; |
+ attrib_pair += 2) { |
+ switch (attrib_pair[0]) { |
+ case NP3DAttrib_PutOffset: |
+ context->putOffset = attrib_pair[1]; |
+ break; |
+ default: |
+ return NPERR_GENERIC_ERROR; |
+ } |
+ } |
+ } |
+ |
+ // Use existing flush mechanism for now. |
+ if (mode != NPDeviceSynchronizationMode_Cached) { |
+ context->waitForProgress = mode == NPDeviceSynchronizationMode_Flush; |
+ Device3DFlushContext(id, context, callback, callback_data); |
+ } |
+ |
+ // Copy most recent output attributes from context. |
+ // To read output attributes after the completion of an asynchronous flush, |
+ // invoke SynchronizeContext again with mode |
+ // NPDeviceSynchronizationMode_Cached from the callback function. |
+ if (output_attrib_list) { |
+ for (int32* attrib_pair = output_attrib_list; |
+ *attrib_pair; |
+ attrib_pair += 2) { |
+ switch (attrib_pair[0]) { |
+ case NP3DAttrib_CommandBufferSize: |
+ attrib_pair[1] = context->commandBufferSize; |
+ break; |
+ case NP3DAttrib_GetOffset: |
+ attrib_pair[1] = context->getOffset; |
+ break; |
+ case NP3DAttrib_PutOffset: |
+ attrib_pair[1] = context->putOffset; |
+ break; |
+ case NP3DAttrib_Token: |
+ attrib_pair[1] = context->token; |
+ break; |
+ default: |
+ return NPERR_GENERIC_ERROR; |
+ } |
+ } |
+ } |
+ |
+ return NPERR_NO_ERROR; |
} |
NPError WebPluginDelegatePepper::DeviceAudioQueryCapability(int32 capability, |
@@ -1038,11 +1383,16 @@ |
: render_view_(render_view), |
plugin_(NULL), |
instance_(instance), |
+ nested_delegate_(NULL), |
current_printer_dpi_(-1), |
#if defined (OS_LINUX) |
num_pages_(0), |
pdf_output_done_(false), |
#endif // (OS_LINUX) |
+#if defined(ENABLE_GPU) |
+ command_buffer_(NULL), |
+ method_factory3d_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
+#endif |
find_identifier_(-1), |
current_choose_file_callback_(NULL), |
current_choose_file_user_data_(NULL) { |
@@ -1080,16 +1430,21 @@ |
void WebPluginDelegatePepper::Paint(WebKit::WebCanvas* canvas, |
const gfx::Rect& rect) { |
- // Blit from background_context to context. |
- if (!committed_bitmap_.isNull()) { |
+ if (nested_delegate_) { |
+ // TODO(apatrick): The GPU plugin will render to an offscreen render target. |
+ // Need to copy it to the screen here. |
+ } else { |
+ // Blit from background_context to context. |
+ if (!committed_bitmap_.isNull()) { |
#if defined(OS_MACOSX) |
- DrawSkBitmapToCanvas(committed_bitmap_, canvas, window_rect_, |
- static_cast<int>(CGBitmapContextGetHeight(canvas))); |
+ DrawSkBitmapToCanvas(committed_bitmap_, canvas, window_rect_, |
+ static_cast<int>(CGBitmapContextGetHeight(canvas))); |
#else |
- canvas->drawBitmap(committed_bitmap_, |
- SkIntToScalar(window_rect_.origin().x()), |
- SkIntToScalar(window_rect_.origin().y())); |
+ canvas->drawBitmap(committed_bitmap_, |
+ SkIntToScalar(window_rect_.origin().x()), |
+ SkIntToScalar(window_rect_.origin().y())); |
#endif |
+ } |
} |
} |
@@ -1229,6 +1584,70 @@ |
return rv; |
} |
+#if defined(ENABLE_GPU) |
+ |
+void WebPluginDelegatePepper::ScheduleHandleRepaint( |
+ NPP npp, NPDeviceContext3D* context) { |
+ command_buffer_->SetNotifyRepaintTask(method_factory3d_.NewRunnableMethod( |
+ &WebPluginDelegatePepper::ForwardHandleRepaint, |
+ npp, |
+ context)); |
+} |
+ |
+void WebPluginDelegatePepper::ForwardHandleRepaint( |
+ NPP npp, NPDeviceContext3D* context) { |
+ if (context->repaintCallback) |
+ context->repaintCallback(npp, context); |
+ ScheduleHandleRepaint(npp, context); |
+} |
+ |
+void WebPluginDelegatePepper::Synchronize3DContext( |
+ NPDeviceContext3D* context, |
+ const gpu::CommandBuffer::State& state) { |
+ context->getOffset = state.get_offset; |
+ context->putOffset = state.put_offset; |
+ context->token = state.token; |
+ context->error = static_cast<NPDeviceContext3DError>(state.error); |
+} |
+ |
+void WebPluginDelegatePepper::Device3DUpdateState( |
+ NPP npp, |
+ NPDeviceContext3D* context, |
+ NPDeviceFlushContextCallbackPtr callback, |
+ void* user_data) { |
+ if (command_buffer_) { |
+ Synchronize3DContext(context, command_buffer_->GetLastState()); |
+ if (callback) |
+ callback(npp, context, NPERR_NO_ERROR, user_data); |
+ } |
+} |
+ |
+#endif // ENABLE_GPU |
+ |
+void WebPluginDelegatePepper::SendNestedDelegateGeometryToBrowser( |
+ const gfx::Rect& window_rect, |
+ const gfx::Rect& clip_rect) { |
+ // Inform the browser about the location of the plugin on the page. |
+ // It appears that initially the plugin does not get laid out correctly -- |
+ // possibly due to lazy creation of the nested delegate. |
+ if (!nested_delegate_ || |
+ !nested_delegate_->GetPluginWindowHandle() || |
+ !render_view_) { |
+ return; |
+ } |
+ |
+ webkit::npapi::WebPluginGeometry geom; |
+ geom.window = nested_delegate_->GetPluginWindowHandle(); |
+ geom.window_rect = window_rect; |
+ geom.clip_rect = clip_rect; |
+ // Rects_valid must be true for this to work in the Gtk port; |
+ // hopefully not having the cutout rects will not cause incorrect |
+ // clipping. |
+ geom.rects_valid = true; |
+ geom.visible = true; |
+ render_view_->DidMovePlugin(geom); |
+} |
+ |
bool WebPluginDelegatePepper::CalculatePrintedPageDimensions( |
int page_number, |
NPPPrintExtensions* print_extensions, |