summaryrefslogtreecommitdiffstats
path: root/o3d/plugin/mac/main_mac.mm
diff options
context:
space:
mode:
authorkbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-11 01:26:00 +0000
committerkbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-11 01:26:00 +0000
commit8a304130d6d53bae89ae6023e64b261624ed5ddf (patch)
tree1b58beab34926aa5eab2f5b2ce866d9a31a6925a /o3d/plugin/mac/main_mac.mm
parentb93cc8295f42e55bb2bec5e444918a8cd20da069 (diff)
downloadchromium_src-8a304130d6d53bae89ae6023e64b261624ed5ddf.zip
chromium_src-8a304130d6d53bae89ae6023e64b261624ed5ddf.tar.gz
chromium_src-8a304130d6d53bae89ae6023e64b261624ed5ddf.tar.bz2
Added support for O3D in Chrome on Mac OS X using CoreGraphics drawing
model by rendering offscreen, reading back the frame buffer, and drawing the rendering results into the CGContextRef. This code path is currently Chrome-specific, but could be used for any browser with similar characteristics. This will require refactoring of the drawing and event model selection code, which may be done in a subsequent bug. Changed the RenderSurface APIs to allow the Bitmap for readback to be passed in. Added Client::SetOffscreenRenderingSurfaces so that the entry point Client::RenderClient() can be used unchanged. Fixed problem with plugin_enable_fullscreen_msg gyp variable which needs to be in top-level gypi so #define is consistent throughout project. Fixed minor issue in Cocoa key event handling. Fixed log message causing crashes when render target attachment fails. Chrome currently blacklists the O3D plugin's MIME type, so to enable support for O3D this blacklist entry needs to be removed from Chrome. Ran nearly all O3D samples in Chrome on Mac OS X. There are a couple of failures which will be fixed in subsequent bugs. Also ran samples in Safari and Firefox on Mac and verified no performance regressions. BUG=http://code.google.com/p/o3d/issues/detail?id=215 TEST=ran O3D samples in Chrome, Safari and Firefox Review URL: http://codereview.chromium.org/669220 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41233 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/plugin/mac/main_mac.mm')
-rw-r--r--o3d/plugin/mac/main_mac.mm186
1 files changed, 164 insertions, 22 deletions
diff --git a/o3d/plugin/mac/main_mac.mm b/o3d/plugin/mac/main_mac.mm
index 0241edb..a07b32f 100644
--- a/o3d/plugin/mac/main_mac.mm
+++ b/o3d/plugin/mac/main_mac.mm
@@ -50,6 +50,7 @@
#include "statsreport/metrics.h"
#include "plugin/cross/plugin_logging.h"
#include "plugin/cross/plugin_metrics.h"
+#include "plugin/cross/o3d_glue.h"
#include "plugin/cross/out_of_memory.h"
#include "plugin/cross/whitelist.h"
#include "plugin/mac/plugin_mac.h"
@@ -62,8 +63,10 @@ bool g_logging_initialized = false;
using glue::_o3d::PluginObject;
using glue::StreamManager;
+using o3d::Bitmap;
using o3d::DisplayWindowMac;
using o3d::Event;
+using o3d::Renderer;
namespace {
// We would normally make this a stack variable in main(), but in a
@@ -77,8 +80,53 @@ base::AtExitManager g_at_exit_manager;
#define CFTIMER
// #define DEFERRED_DRAW_ON_NULLEVENTS
-void DrawPlugin(PluginObject* obj, bool send_callback) {
+void DrawPlugin(PluginObject* obj, bool send_callback, CGContextRef context) {
obj->client()->RenderClient(send_callback);
+ Renderer* renderer = obj->renderer();
+ if (obj->IsOffscreenRenderingEnabled() && renderer && context) {
+ DCHECK_EQ(obj->drawing_model_, NPDrawingModelCoreGraphics);
+ DCHECK(obj->mac_cgl_pbuffer_);
+ // We need to read back the framebuffer and draw it to the screen using
+ // CoreGraphics.
+ renderer->StartRendering();
+ Bitmap::Ref bitmap = obj->GetOffscreenBitmap();
+ obj->GetOffscreenRenderSurface()->GetIntoBitmap(bitmap);
+ bitmap->FlipVertically();
+ renderer->FinishRendering();
+ uint8* data = bitmap->GetMipData(0);
+ unsigned width = bitmap->width();
+ unsigned height = bitmap->height();
+ int rowBytes = width * 4;
+ CGContextSaveGState(context);
+
+ CGDataProviderRef dataProvider =
+ CGDataProviderCreateWithData(0, data, rowBytes * height, 0);
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ // We need to use kCGImageAlphaNoneSkipFirst to discard the alpha channel.
+ // O3D's output is currently semantically opaque.
+ CGImageRef cgImage = CGImageCreate(width,
+ height,
+ 8,
+ 32,
+ rowBytes,
+ colorSpace,
+ kCGImageAlphaNoneSkipFirst |
+ kCGBitmapByteOrder32Host,
+ dataProvider,
+ 0,
+ false,
+ kCGRenderingIntentDefault);
+ CGRect rect = CGRectMake(0, 0, width, height);
+ // We want to completely overwrite the previous frame's
+ // rendering results.
+ CGContextSetBlendMode(context, kCGBlendModeCopy);
+ CGContextSetInterpolationQuality(context, kCGInterpolationNone);
+ CGContextDrawImage(context, rect, cgImage);
+ CGImageRelease(cgImage);
+ CGColorSpaceRelease(colorSpace);
+ CGDataProviderRelease(dataProvider);
+ CGContextRestoreGState(context);
+ }
}
unsigned char GetMacEventKeyChar(const EventRecord *the_event) {
@@ -418,7 +466,11 @@ bool HandleCocoaEvent(NPP instance, NPCocoaEvent* the_event) {
obj->MacEventReceived();
switch (the_event->type) {
case NPCocoaEventDrawRect:
- DrawPlugin(obj, false);
+ // We need to call the render callback from here if we are rendering
+ // off-screen because it doesn't get called anywhere else.
+ DrawPlugin(obj,
+ obj->IsOffscreenRenderingEnabled(),
+ the_event->data.draw.context);
handled = true;
break;
case NPCocoaEventMouseDown:
@@ -436,6 +488,10 @@ bool HandleCocoaEvent(NPP instance, NPCocoaEvent* the_event) {
NSString *chars =
(NSString*) the_event->data.key.charactersIgnoringModifiers;
+ if (chars == NULL || [chars length] == 0) {
+ break;
+ }
+
if ([chars characterAtIndex:0] == '\e') {
obj->CancelFullscreenDisplay();
break;
@@ -451,6 +507,10 @@ bool HandleCocoaEvent(NPP instance, NPCocoaEvent* the_event) {
NSString *chars =
(NSString*) the_event->data.key.charactersIgnoringModifiers;
+ if (chars == NULL || [chars length] == 0) {
+ break;
+ }
+
DispatchKeyboardEvent(obj,
eventKind,
[chars characterAtIndex:0],
@@ -574,6 +634,11 @@ void Mac_SetBestEventModel(NPP instance, PluginObject* obj) {
// AGL context to the browser window.
model_to_use =
(supportsCarbonEventModel) ? NPEventModelCarbon : NPEventModelCocoa;
+ if (o3d::gIsChrome) {
+ if (supportsCocoaEventModel) {
+ model_to_use = NPEventModelCocoa;
+ }
+ }
NPN_SetValue(instance, NPPVpluginEventModel,
reinterpret_cast<void*>(model_to_use));
obj->event_model_ = model_to_use;
@@ -611,21 +676,30 @@ NPError Mac_SetBestDrawingModel(NPP instance, PluginObject* obj) {
if (err != NPERR_NO_ERROR)
supportsCoreGraphics = FALSE;
-
- // In order of preference. Preference is now determined by compatibility,
- // not by modernity, and so is the opposite of the order I first used.
- if (supportsQuickDraw && !(obj->event_model_ == NPEventModelCocoa)) {
- drawing_model = NPDrawingModelQuickDraw;
- } else if (supportsCoreGraphics) {
+ // In the Chrome browser we currently want to prefer the CoreGraphics
+ // drawing model, read back the frame buffer into system memory and draw
+ // the results to the screen using CG.
+ //
+ // TODO(maf): Once support for the CoreAnimation drawing model is
+ // integrated into O3D, we will want to revisit this logic.
+ if (o3d::gIsChrome && supportsCoreGraphics) {
drawing_model = NPDrawingModelCoreGraphics;
- } else if (supportsOpenGL) {
- drawing_model = NPDrawingModelOpenGL;
} else {
- // This case is for browsers that didn't even understand the question
- // eg FF2, so drawing models are not supported, just assume QuickDraw.
- obj->drawing_model_ = NPDrawingModelQuickDraw;
- return NPERR_NO_ERROR;
- }
+ // In order of preference. Preference is now determined by compatibility,
+ // not by modernity, and so is the opposite of the order I first used.
+ if (supportsQuickDraw && !(obj->event_model_ == NPEventModelCocoa)) {
+ drawing_model = NPDrawingModelQuickDraw;
+ } else if (supportsCoreGraphics) {
+ drawing_model = NPDrawingModelCoreGraphics;
+ } else if (supportsOpenGL) {
+ drawing_model = NPDrawingModelOpenGL;
+ } else {
+ // This case is for browsers that didn't even understand the question
+ // eg FF2, so drawing models are not supported, just assume QuickDraw.
+ obj->drawing_model_ = NPDrawingModelQuickDraw;
+ return NPERR_NO_ERROR;
+ }
+ }
err = NPN_SetValue(instance, NPPVpluginDrawingModel,
reinterpret_cast<void*>(drawing_model));
@@ -731,7 +805,9 @@ bool HandleMacEvent(EventRecord* the_event, NPP instance) {
GLUE_PROFILE_STOP(instance, "forceredraw");
#elif defined(CFTIMER)
#else
- DrawPlugin(obj, true);
+ DrawPlugin(obj, true,
+ (obj->drawing_model_ == NPDrawingModelCoreGraphics) ?
+ reinterpret_cast<CGContextRef>(obj->mac_2d_context_) : NULL);
#endif
// Safari tab switching recovery code.
if (obj->mac_surface_hidden_) {
@@ -756,7 +832,9 @@ bool HandleMacEvent(EventRecord* the_event, NPP instance) {
handled = true;
break;
case updateEvt:
- DrawPlugin(obj, false);
+ DrawPlugin(obj, false,
+ (obj->drawing_model_ == NPDrawingModelCoreGraphics) ?
+ reinterpret_cast<CGContextRef>(obj->mac_2d_context_) : NULL);
handled = true;
break;
case osEvt:
@@ -853,7 +931,6 @@ bool CheckForAGLError() {
return aglGetError() != AGL_NO_ERROR;
}
-
NPError NPP_SetWindow(NPP instance, NPWindow* window) {
HANDLE_CRASHES;
PluginObject* obj = static_cast<PluginObject*>(instance->pdata);
@@ -861,7 +938,8 @@ NPError NPP_SetWindow(NPP instance, NPWindow* window) {
assert(window != NULL);
- if (window->window == NULL)
+ if (window->window == NULL &&
+ obj->drawing_model_ != NPDrawingModelCoreGraphics)
return NPERR_NO_ERROR;
obj->last_plugin_loc_.h = window->x;
@@ -918,11 +996,63 @@ NPError NPP_SetWindow(NPP instance, NPWindow* window) {
// Whether we already had a window before this call.
bool had_a_window = obj->mac_window_ != NULL;
+ // Whether we already had a pbuffer before this call.
+ bool had_a_pbuffer = obj->mac_cgl_pbuffer_ != NULL;
+
obj->mac_window_ = new_window;
if (obj->drawing_model_ == NPDrawingModelOpenGL) {
CGLSetCurrentContext(obj->mac_cgl_context_);
- } else if (!had_a_window && obj->mac_agl_context_ == NULL) { // setup AGL context
+ } else if (obj->drawing_model_ == NPDrawingModelCoreGraphics &&
+ o3d::gIsChrome &&
+ obj->mac_cgl_pbuffer_ == NULL) {
+ // This code path is only taken for Chrome. We initialize things with a
+ // CGL context rendering to a 1x1 pbuffer. Later we use the O3D
+ // RenderSurface APIs to set up the framebuffer object which is used
+ // for rendering.
+ static const CGLPixelFormatAttribute attribs[] = {
+ (CGLPixelFormatAttribute) kCGLPFAPBuffer,
+ (CGLPixelFormatAttribute) 0
+ };
+ CGLPixelFormatObj pixelFormat;
+ GLint numPixelFormats;
+ if (CGLChoosePixelFormat(attribs,
+ &pixelFormat,
+ &numPixelFormats) != kCGLNoError) {
+ DLOG(ERROR) << "Error choosing pixel format.";
+ return NPERR_GENERIC_ERROR;
+ }
+ if (!pixelFormat) {
+ DLOG(ERROR) << "Unable to find pbuffer compatible pixel format.";
+ return NPERR_GENERIC_ERROR;
+ }
+ CGLContextObj context;
+ CGLError res = CGLCreateContext(pixelFormat, 0, &context);
+ CGLDestroyPixelFormat(pixelFormat);
+ if (res != kCGLNoError) {
+ DLOG(ERROR) << "Error creating context.";
+ return NPERR_GENERIC_ERROR;
+ }
+ CGLPBufferObj pbuffer;
+ if (CGLCreatePBuffer(1, 1,
+ GL_TEXTURE_2D, GL_RGBA,
+ 0, &pbuffer) != kCGLNoError) {
+ CGLDestroyContext(context);
+ DLOG(ERROR) << "Error creating pbuffer.";
+ return NPERR_GENERIC_ERROR;
+ }
+ if (CGLSetPBuffer(context, pbuffer, 0, 0, 0) != kCGLNoError) {
+ CGLDestroyContext(context);
+ CGLDestroyPBuffer(pbuffer);
+ DLOG(ERROR) << "Error attaching pbuffer to context.";
+ return NPERR_GENERIC_ERROR;
+ }
+ // Must make the context current for renderer creation to succeed
+ CGLSetCurrentContext(context);
+ obj->mac_cgl_context_ = context;
+ obj->mac_cgl_pbuffer_ = pbuffer;
+ } else if (!had_a_window && obj->mac_agl_context_ == NULL) {
+ // setup AGL context
AGLPixelFormat myAGLPixelFormat = NULL;
// We need to spec out a few similar but different sets of renderer
@@ -1091,6 +1221,14 @@ NPError NPP_SetWindow(NPP instance, NPWindow* window) {
aglEnable(obj->mac_agl_context_, AGL_BUFFER_RECT);
}
+ if (had_a_pbuffer) {
+ // CoreGraphics drawing model when we have no on-screen window (Chrome,
+ // specifically).
+ obj->EnableOffscreenRendering();
+ obj->Resize(window->width, window->height);
+ return NPERR_NO_ERROR;
+ }
+
// Renderer is already initialized from a previous call to this function,
// just update size and position and return.
if (had_a_window) {
@@ -1120,9 +1258,13 @@ NPError NPP_SetWindow(NPP instance, NPWindow* window) {
obj->client()->Init();
if (obj->renderer()) {
- obj->renderer()->SetClientOriginOffset(gl_x_origin, gl_y_origin);
- obj->Resize(window->width, window->height);
+ if (obj->mac_cgl_pbuffer_) {
+ obj->EnableOffscreenRendering();
+ } else {
+ obj->renderer()->SetClientOriginOffset(gl_x_origin, gl_y_origin);
+ }
+ obj->Resize(window->width, window->height);
#ifdef CFTIMER
// now that the grahics context is setup, add this instance to the timer
// list so it gets drawn repeatedly