summaryrefslogtreecommitdiffstats
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
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
-rw-r--r--o3d/build/common.gypi10
-rw-r--r--o3d/core/cross/client.cc27
-rw-r--r--o3d/core/cross/client.h12
-rw-r--r--o3d/core/cross/gl/render_surface_gl.cc12
-rw-r--r--o3d/core/cross/gl/render_surface_gl.h4
-rw-r--r--o3d/core/cross/gl/renderer_gl.cc3
-rw-r--r--o3d/core/cross/gl/texture_gl.cc1
-rw-r--r--o3d/core/cross/param_test.cc2
-rw-r--r--o3d/core/cross/render_surface.cc24
-rw-r--r--o3d/core/cross/render_surface.h11
-rw-r--r--o3d/core/win/d3d9/render_surface_d3d9.cc26
-rw-r--r--o3d/core/win/d3d9/render_surface_d3d9.h4
-rw-r--r--o3d/plugin/cross/o3d_glue.cc104
-rw-r--r--o3d/plugin/cross/o3d_glue.h37
-rw-r--r--o3d/plugin/mac/config_mac.mm1
-rw-r--r--o3d/plugin/mac/main_mac.mm186
-rw-r--r--o3d/plugin/mac/plugin_mac.h1
-rw-r--r--o3d/plugin/mac/plugin_mac.mm21
-rw-r--r--o3d/plugin/plugin.gyp9
19 files changed, 429 insertions, 66 deletions
diff --git a/o3d/build/common.gypi b/o3d/build/common.gypi
index ee2324f..4c319e4 100644
--- a/o3d/build/common.gypi
+++ b/o3d/build/common.gypi
@@ -71,6 +71,9 @@
# Add a way to disable FBO support for GL implementations that don't have
# it.
'disable_fbo%': 0,
+
+ # Whether to enable the English-only, Win/Mac-only fullscreen message.
+ 'plugin_enable_fullscreen_msg%': '1',
},
'target_defaults': {
'defines': [
@@ -135,6 +138,13 @@
],
},
],
+ ['<(plugin_enable_fullscreen_msg) != 0',
+ {
+ 'defines': [
+ 'O3D_PLUGIN_ENABLE_FULLSCREEN_MSG=1',
+ ],
+ },
+ ],
],
},
'conditions' : [
diff --git a/o3d/core/cross/client.cc b/o3d/core/cross/client.cc
index a7f5924..c63c940 100644
--- a/o3d/core/cross/client.cc
+++ b/o3d/core/cross/client.cc
@@ -304,7 +304,25 @@ void Client::RenderClient(bool send_callback) {
if (!renderer_.IsAvailable())
return;
- RenderClientInner(true, send_callback);
+ bool have_offscreen_surfaces =
+ !(offscreen_render_surface_.IsNull() ||
+ offscreen_depth_render_surface_.IsNull());
+
+ if (have_offscreen_surfaces) {
+ if (!renderer_->StartRendering()) {
+ return;
+ }
+ renderer_->SetRenderSurfaces(offscreen_render_surface_,
+ offscreen_depth_render_surface_,
+ true);
+ }
+
+ RenderClientInner(!have_offscreen_surfaces, send_callback);
+
+ if (have_offscreen_surfaces) {
+ renderer_->SetRenderSurfaces(NULL, NULL, false);
+ renderer_->FinishRendering();
+ }
}
// Executes draw calls for all visible shapes in a subtree
@@ -503,6 +521,13 @@ String Client::GetMessageQueueAddress() const {
}
}
+void Client::SetOffscreenRenderingSurfaces(
+ RenderSurface::Ref surface,
+ RenderDepthStencilSurface::Ref depth_surface) {
+ offscreen_render_surface_ = surface;
+ offscreen_depth_render_surface_ = depth_surface;
+}
+
// Error Related methods -------------------------------------------------------
void Client::SetErrorCallback(ErrorCallback* callback) {
diff --git a/o3d/core/cross/client.h b/o3d/core/cross/client.h
index 7347af4..003c6de 100644
--- a/o3d/core/cross/client.h
+++ b/o3d/core/cross/client.h
@@ -59,6 +59,7 @@
#include "core/cross/event_manager.h"
#include "core/cross/lost_resource_callback.h"
#include "core/cross/render_event.h"
+#include "core/cross/render_surface.h"
#include "core/cross/tick_event.h"
#include "core/cross/timer.h"
#include "core/cross/timingtable.h"
@@ -429,6 +430,14 @@ class Client {
DISALLOW_COPY_AND_ASSIGN(ScopedIncrement);
};
+ // Offscreen rendering methods -------------------
+
+ // Sets up this Client so that RenderClient will cause the rendering
+ // results to go into the given surfaces.
+ void SetOffscreenRenderingSurfaces(
+ RenderSurface::Ref surface,
+ RenderDepthStencilSurface::Ref depth_surface);
+
private:
// Renders the client.
void RenderClientInner(bool present, bool send_callback);
@@ -499,6 +508,9 @@ class Client {
int calls_; // Used to check reentrancy along with ScopedIncrement.
+ RenderSurface::Ref offscreen_render_surface_;
+ RenderDepthStencilSurface::Ref offscreen_depth_render_surface_;
+
DISALLOW_COPY_AND_ASSIGN(Client);
}; // Client
diff --git a/o3d/core/cross/gl/render_surface_gl.cc b/o3d/core/cross/gl/render_surface_gl.cc
index 6570baa..f12718c 100644
--- a/o3d/core/cross/gl/render_surface_gl.cc
+++ b/o3d/core/cross/gl/render_surface_gl.cc
@@ -54,13 +54,13 @@ RenderSurfaceGL::RenderSurfaceGL(ServiceLocator *service_locator,
RenderSurfaceGL::~RenderSurfaceGL() {
}
-Bitmap::Ref RenderSurfaceGL::PlatformSpecificGetBitmap() const {
+bool RenderSurfaceGL::PlatformSpecificGetIntoBitmap(Bitmap::Ref bitmap) const {
Renderer* renderer = service_locator()->GetService<Renderer>();
DCHECK(renderer);
-
- Bitmap::Ref bitmap = Bitmap::Ref(new Bitmap(service_locator()));
- bitmap->Allocate(
- Texture::ARGB8, clip_width(), clip_height(), 1, Bitmap::IMAGE);
+ DCHECK(bitmap->width() == static_cast<unsigned int>(clip_width()) &&
+ bitmap->height() == static_cast<unsigned int>(clip_height()) &&
+ bitmap->num_mipmaps() == 1 &&
+ bitmap->format() == Texture::ARGB8);
const RenderSurface* old_render_surface;
const RenderDepthStencilSurface* old_depth_surface;
@@ -76,7 +76,7 @@ Bitmap::Ref RenderSurfaceGL::PlatformSpecificGetBitmap() const {
renderer->SetRenderSurfaces(old_render_surface, old_depth_surface,
old_is_back_buffer);
- return bitmap;
+ return true;
}
RenderDepthStencilSurfaceGL::RenderDepthStencilSurfaceGL(
diff --git a/o3d/core/cross/gl/render_surface_gl.h b/o3d/core/cross/gl/render_surface_gl.h
index 593b7994..254a142 100644
--- a/o3d/core/cross/gl/render_surface_gl.h
+++ b/o3d/core/cross/gl/render_surface_gl.h
@@ -73,8 +73,8 @@ class RenderSurfaceGL : public RenderSurface {
}
protected:
- // The platform specific part of GetBitmap.
- virtual Bitmap::Ref PlatformSpecificGetBitmap() const;
+ // The platform specific part of GetIntoBitmap.
+ virtual bool PlatformSpecificGetIntoBitmap(Bitmap::Ref bitmap) const;
private:
GLenum cube_face_;
diff --git a/o3d/core/cross/gl/renderer_gl.cc b/o3d/core/cross/gl/renderer_gl.cc
index e4de52f..378c74e 100644
--- a/o3d/core/cross/gl/renderer_gl.cc
+++ b/o3d/core/cross/gl/renderer_gl.cc
@@ -1317,7 +1317,8 @@ void RendererGL::SetRenderSurfacesPlatformSpecific(
if (!InstallFramebufferObjects(surface, surface_depth)) {
O3D_ERROR(service_locator())
<< "Failed to bind OpenGL render target objects:"
- << surface->name() <<", "<< surface_depth->name();
+ << (surface ? surface->name() : "(no surface)") << ", "
+ << (surface_depth ? surface_depth->name() : "(no depth surface)");
}
// RenderSurface rendering is performed with an inverted Y, so the front
// face winding must be changed to clock-wise. See comments for
diff --git a/o3d/core/cross/gl/texture_gl.cc b/o3d/core/cross/gl/texture_gl.cc
index c168f41..f9a11e0 100644
--- a/o3d/core/cross/gl/texture_gl.cc
+++ b/o3d/core/cross/gl/texture_gl.cc
@@ -278,6 +278,7 @@ Texture2DGL* Texture2DGL::Create(ServiceLocator* service_locator,
bool enable_render_surfaces) {
DLOG(INFO) << "Texture2DGL Create";
DCHECK_NE(format, Texture::UNKNOWN_FORMAT);
+ DCHECK_GE(levels, 0);
RendererGL *renderer = static_cast<RendererGL *>(
service_locator->GetService<Renderer>());
renderer->MakeCurrentLazy();
diff --git a/o3d/core/cross/param_test.cc b/o3d/core/cross/param_test.cc
index 82daa16..f4e7299 100644
--- a/o3d/core/cross/param_test.cc
+++ b/o3d/core/cross/param_test.cc
@@ -78,7 +78,7 @@ class TestRenderSurface : public RenderSurface {
explicit TestRenderSurface(ServiceLocator* service_locator)
: RenderSurface(service_locator, 1, 1, NULL) {}
void* GetSurfaceHandle() const { return NULL; }
- Bitmap::Ref PlatformSpecificGetBitmap() const { return Bitmap::Ref(); }
+ bool PlatformSpecificGetIntoBitmap(Bitmap::Ref bitmap) const { return true; }
};
// TestSampler derives from o3d::Sampler and provides a public
diff --git a/o3d/core/cross/render_surface.cc b/o3d/core/cross/render_surface.cc
index 0e6a85a..8db8f01 100644
--- a/o3d/core/cross/render_surface.cc
+++ b/o3d/core/cross/render_surface.cc
@@ -72,7 +72,29 @@ RenderSurface::RenderSurface(ServiceLocator* service_locator,
}
Bitmap::Ref RenderSurface::GetBitmap() const {
- return PlatformSpecificGetBitmap();
+ Bitmap::Ref bitmap = Bitmap::Ref(new Bitmap(service_locator()));
+ bitmap->Allocate(Texture::ARGB8,
+ clip_width(),
+ clip_height(),
+ 1,
+ Bitmap::IMAGE);
+ if (!GetIntoBitmap(bitmap)) {
+ Bitmap::Ref empty;
+ return empty;
+ }
+ return bitmap;
+}
+
+bool RenderSurface::GetIntoBitmap(Bitmap::Ref bitmap) const {
+ if (bitmap.IsNull() ||
+ bitmap->width() != static_cast<unsigned int>(clip_width()) ||
+ bitmap->height() != static_cast<unsigned int>(clip_height()) ||
+ bitmap->num_mipmaps() != 1 ||
+ bitmap->format() != Texture::ARGB8) {
+ return false;
+ }
+
+ return PlatformSpecificGetIntoBitmap(bitmap);
}
RenderDepthStencilSurface::RenderDepthStencilSurface(
diff --git a/o3d/core/cross/render_surface.h b/o3d/core/cross/render_surface.h
index ba6db69..8ee3f01 100644
--- a/o3d/core/cross/render_surface.h
+++ b/o3d/core/cross/render_surface.h
@@ -138,9 +138,16 @@ class RenderSurface : public RenderSurfaceBase {
// Only gets the clip_width/clip_height area.
Bitmap::Ref GetBitmap() const;
+ // Gets a copy of the contents of the render surface into the given
+ // Bitmap. Only gets the clip_width/clip_height area. Returns true
+ // if the bitmap was appropriately sized, false if not or if an
+ // error occurred.
+ bool GetIntoBitmap(Bitmap::Ref bitmap) const;
+
protected:
- // The platform specific part of GetBitmap.
- virtual Bitmap::Ref PlatformSpecificGetBitmap() const = 0;
+ // The platform specific part of GetIntoBitmap.
+ virtual bool PlatformSpecificGetIntoBitmap(
+ Bitmap::Ref bitmap) const = 0;
private:
// Texture parameter of the texture in which this render surface is contained.
diff --git a/o3d/core/win/d3d9/render_surface_d3d9.cc b/o3d/core/win/d3d9/render_surface_d3d9.cc
index 5b7c9e4..c291887 100644
--- a/o3d/core/win/d3d9/render_surface_d3d9.cc
+++ b/o3d/core/win/d3d9/render_surface_d3d9.cc
@@ -75,11 +75,15 @@ void RenderSurfaceD3D9::Clear() {
}
}
-Bitmap::Ref RenderSurfaceD3D9::PlatformSpecificGetBitmap() const {
- Bitmap::Ref empty;
+bool RenderSurfaceD3D9::PlatformSpecificGetIntoBitmap(
+ Bitmap::Ref bitmap) const {
+ DCHECK(bitmap->width() == static_cast<unsigned int>(clip_width()) &&
+ bitmap->height() == static_cast<unsigned int>(clip_height()) &&
+ bitmap->num_mipmaps() == 1 &&
+ bitmap->format() == Texture::ARGB8);
if (!direct3d_surface_) {
- return empty;
+ return false;
}
RendererD3D9* renderer =
@@ -89,7 +93,7 @@ Bitmap::Ref RenderSurfaceD3D9::PlatformSpecificGetBitmap() const {
D3DSURFACE_DESC surface_description;
if (!HR(direct3d_surface_->GetDesc(&surface_description))) {
- return empty;
+ return false;
}
if (!HR(device->CreateOffscreenPlainSurface(surface_description.Width,
@@ -98,25 +102,19 @@ Bitmap::Ref RenderSurfaceD3D9::PlatformSpecificGetBitmap() const {
D3DPOOL_SYSTEMMEM,
&system_surface,
NULL))) {
- return empty;
+ return false;
}
if (!HR(device->GetRenderTargetData(direct3d_surface_, system_surface)))
- return empty;
+ return false;
RECT rect = { 0, 0, clip_width(), clip_height() };
D3DLOCKED_RECT out_rect = {0};
if (!HR(system_surface->LockRect(&out_rect, &rect, D3DLOCK_READONLY))) {
O3D_ERROR(service_locator()) << "Failed to Lock Surface (D3D9)";
- return empty;
+ return false;
}
- Bitmap::Ref bitmap = Bitmap::Ref(new Bitmap(service_locator()));
- bitmap->Allocate(Texture::ARGB8,
- clip_width(),
- clip_height(),
- 1,
- Bitmap::IMAGE);
bitmap->SetRect(0, 0, 0,
clip_width(),
clip_height(),
@@ -125,7 +123,7 @@ Bitmap::Ref RenderSurfaceD3D9::PlatformSpecificGetBitmap() const {
system_surface->UnlockRect();
- return bitmap;
+ return true;
}
RenderDepthStencilSurfaceD3D9::RenderDepthStencilSurfaceD3D9(
diff --git a/o3d/core/win/d3d9/render_surface_d3d9.h b/o3d/core/win/d3d9/render_surface_d3d9.h
index c708dab..f541e6f 100644
--- a/o3d/core/win/d3d9/render_surface_d3d9.h
+++ b/o3d/core/win/d3d9/render_surface_d3d9.h
@@ -79,8 +79,8 @@ class RenderSurfaceD3D9 : public RenderSurface {
void Clear();
protected:
- // The platform specific part of GetBitmap.
- virtual Bitmap::Ref PlatformSpecificGetBitmap() const;
+ // The platform specific part of GetIntoBitmap.
+ virtual bool PlatformSpecificGetIntoBitmap(Bitmap::Ref bitmap) const;
private:
CComPtr<IDirect3DSurface9> direct3d_surface_;
diff --git a/o3d/plugin/cross/o3d_glue.cc b/o3d/plugin/cross/o3d_glue.cc
index 6a2d53f..0d66e9c 100644
--- a/o3d/plugin/cross/o3d_glue.cc
+++ b/o3d/plugin/cross/o3d_glue.cc
@@ -37,7 +37,9 @@
#include <string>
#include <algorithm>
+#include "core/cross/image_utils.h"
#include "core/cross/renderer.h"
+#include "core/cross/texture.h"
#include "core/cross/client_info.h"
#include "plugin/cross/o3d_glue.h"
#include "plugin/cross/config.h"
@@ -56,6 +58,10 @@
namespace glue {
namespace _o3d {
+using o3d::Bitmap;
+using o3d::Texture;
+using o3d::Texture2D;
+
void RegisterType(NPP npp, const ObjectBase::Class *clientclass,
NPClass *npclass) {
PluginObject *plugin_object = static_cast<PluginObject *>(npp->pdata);
@@ -134,6 +140,7 @@ PluginObject::PluginObject(NPP npp)
mac_2d_context_(0),
mac_agl_context_(0),
mac_cgl_context_(0),
+ mac_cgl_pbuffer_(0),
last_mac_event_time_(0),
#ifdef O3D_PLUGIN_ENABLE_FULLSCREEN_MSG
time_to_hide_overlay_(0.0),
@@ -165,7 +172,8 @@ PluginObject::PluginObject(NPP npp)
stream_manager_(new StreamManager(npp)),
cursor_type_(o3d::Cursor::DEFAULT),
prev_width_(0),
- prev_height_(0) {
+ prev_height_(0),
+ offscreen_rendering_enabled_(false) {
#if defined(OS_WIN) || defined(OS_LINUX)
memset(cursors_, 0, sizeof(cursors_));
#endif
@@ -218,6 +226,7 @@ void PluginObject::Init(int argc, char* argn[], char* argv[]) {
}
void PluginObject::TearDown() {
+ DisableOffscreenRendering();
#ifdef OS_WIN
ClearPluginProperty(hWnd_);
#elif defined(OS_MACOSX)
@@ -717,6 +726,12 @@ void PluginObject::Resize(int width, int height) {
prev_height_ = height;
if (renderer_ && !fullscreen_) {
+ // If we are rendering offscreen, we may need to reallocate the
+ // render surfaces.
+ if (offscreen_rendering_enabled_) {
+ AllocateOffscreenRenderSurfaces(width, height);
+ }
+
// Tell the renderer and client that our window has been resized.
// If we're in fullscreen mode when this happens, we don't want to pass
// the information through; the renderer will pick it up when we switch
@@ -1053,6 +1068,93 @@ void PluginObject::Tick() {
}
}
+void PluginObject::EnableOffscreenRendering() {
+ if (!offscreen_rendering_enabled_) {
+ AllocateOffscreenRenderSurfaces(width(), height());
+ offscreen_rendering_enabled_ = true;
+ }
+}
+
+void PluginObject::DisableOffscreenRendering() {
+ if (offscreen_rendering_enabled_) {
+ DeallocateOffscreenRenderSurfaces();
+ offscreen_rendering_enabled_ = false;
+ }
+}
+
+bool PluginObject::IsOffscreenRenderingEnabled() const {
+ return offscreen_rendering_enabled_;
+}
+
+RenderSurface::Ref PluginObject::GetOffscreenRenderSurface() const {
+ return offscreen_render_surface_;
+}
+
+Bitmap::Ref PluginObject::GetOffscreenBitmap() const {
+ return offscreen_readback_bitmap_;
+}
+
+bool PluginObject::AllocateOffscreenRenderSurfaces(int width, int height) {
+ int pot_width =
+ static_cast<int>(o3d::image::ComputePOTSize(width));
+ int pot_height =
+ static_cast<int>(o3d::image::ComputePOTSize(height));
+ if (!renderer_ || pot_width == 0 || pot_height == 0) {
+ return false;
+ }
+ bool must_reallocate_render_surfaces =
+ (offscreen_render_surface_.IsNull() ||
+ offscreen_depth_render_surface_.IsNull() ||
+ offscreen_render_surface_->width() != pot_width ||
+ offscreen_render_surface_->height() != pot_height);
+ if (must_reallocate_render_surfaces) {
+ Texture2D::Ref texture = renderer_->CreateTexture2D(
+ pot_width,
+ pot_height,
+ Texture::ARGB8,
+ 1,
+ true);
+ if (texture.IsNull()) {
+ return false;
+ }
+ RenderSurface::Ref surface(texture->GetRenderSurface(0));
+ if (surface.IsNull()) {
+ return false;
+ }
+ RenderDepthStencilSurface::Ref depth(renderer_->CreateDepthStencilSurface(
+ pot_width,
+ pot_height));
+ if (depth.IsNull()) {
+ return false;
+ }
+ offscreen_texture_ = texture;
+ offscreen_render_surface_ = surface;
+ offscreen_depth_render_surface_ = depth;
+ }
+ offscreen_render_surface_->SetClipSize(width, height);
+ offscreen_depth_render_surface_->SetClipSize(width, height);
+ if (offscreen_readback_bitmap_.IsNull() ||
+ offscreen_readback_bitmap_->width() != width ||
+ offscreen_readback_bitmap_->height() != height) {
+ o3d::Bitmap::Ref bitmap = Bitmap::Ref(
+ new Bitmap(service_locator()));
+ bitmap->Allocate(Texture::ARGB8,
+ width, height, 1, Bitmap::IMAGE);
+ offscreen_readback_bitmap_ = bitmap;
+ }
+ // Tell the Client about the newly allocated surfaces so that normal
+ // calls to RenderClient can automatically do the right thing.
+ client_->SetOffscreenRenderingSurfaces(offscreen_render_surface_,
+ offscreen_depth_render_surface_);
+ return true;
+}
+
+void PluginObject::DeallocateOffscreenRenderSurfaces() {
+ offscreen_render_surface_.Reset();
+ offscreen_depth_render_surface_.Reset();
+ offscreen_readback_bitmap_.Reset();
+}
+
} // namespace _o3d
namespace globals {
diff --git a/o3d/plugin/cross/o3d_glue.h b/o3d/plugin/cross/o3d_glue.h
index 8911804..c335665 100644
--- a/o3d/plugin/cross/o3d_glue.h
+++ b/o3d/plugin/cross/o3d_glue.h
@@ -53,6 +53,7 @@
#include <vector>
#include "base/scoped_ptr.h"
#include "base/hash_tables.h"
+#include "core/cross/bitmap.h"
#include "core/cross/display_mode.h"
#include "core/cross/display_window.h"
#include "core/cross/object_base.h"
@@ -65,6 +66,8 @@
#include "core/cross/object_manager.h"
#include "core/cross/error.h"
#include "core/cross/profiler.h"
+#include "core/cross/render_surface.h"
+#include "core/cross/texture.h"
#include "plugin/cross/main_thread_task_poster.h"
#include "plugin/cross/np_v8_bridge.h"
#include "client_glue.h"
@@ -116,6 +119,7 @@ class StreamManager;
namespace _o3d {
using o3d::Id;
using o3d::ObjectBase;
+using o3d::Bitmap;
using o3d::Client;
using o3d::ClassManager;
using o3d::ClientInfoManager;
@@ -126,7 +130,10 @@ using o3d::MainThreadTaskPoster;
using o3d::ObjectManager;
using o3d::Profiler;
using o3d::Renderer;
+using o3d::RenderSurface;
+using o3d::RenderDepthStencilSurface;
using o3d::ServiceLocator;
+using o3d::Texture2D;
class NPAPIObject: public NPObject {
NPP npp_;
@@ -268,6 +275,9 @@ class PluginObject: public NPObject {
// either can be NULL depending on drawing_model
AGLContext mac_agl_context_;
CGLContextObj mac_cgl_context_;
+ // If in Chrome, we actually do all of our rendering offscreen, and
+ // bootstrap off a 1x1 pbuffer
+ CGLPBufferObj mac_cgl_pbuffer_;
// Fullscreen related stuff.
@@ -475,6 +485,19 @@ class PluginObject: public NPObject {
}
#endif
+ // Support for rendering the plugin's content into render surfaces.
+ // This is currently different than offscreen rendering support in
+ // the renderer. It is a bit of a mess because only the PluginObject
+ // and Renderer have the necessary information about the viewport
+ // size, and only the Client has enough information to properly
+ // initiate a render.
+ void EnableOffscreenRendering();
+ void DisableOffscreenRendering();
+ bool IsOffscreenRenderingEnabled() const;
+ RenderSurface::Ref GetOffscreenRenderSurface() const;
+ RenderDepthStencilSurface::Ref GetOffscreenDepthRenderSurface() const;
+ Bitmap::Ref GetOffscreenBitmap() const;
+
private:
bool fullscreen_region_valid_;
int fullscreen_region_x_;
@@ -497,6 +520,20 @@ class PluginObject: public NPObject {
#if defined(CB_SERVICE_REMOTE)
NPObject* gpu_plugin_object_;
#endif
+
+ // Support for offscreen rendering and thereby windowless plugins.
+ // Currently used only for the CoreGraphics drawing model on Mac OS
+ // X when there is no WindowRef. It would be nicer to put this in
+ // the Renderer and/or make its off_screen mode work everywhere, but
+ // this is only a legacy solution for older browsers.
+ bool offscreen_rendering_enabled_;
+ Texture2D::Ref offscreen_texture_;
+ RenderSurface::Ref offscreen_render_surface_;
+ RenderDepthStencilSurface::Ref offscreen_depth_render_surface_;
+ Bitmap::Ref offscreen_readback_bitmap_;
+
+ bool AllocateOffscreenRenderSurfaces(int width, int height);
+ void DeallocateOffscreenRenderSurfaces();
};
} // namespace o3d
diff --git a/o3d/plugin/mac/config_mac.mm b/o3d/plugin/mac/config_mac.mm
index c85f8c8..c137df6 100644
--- a/o3d/plugin/mac/config_mac.mm
+++ b/o3d/plugin/mac/config_mac.mm
@@ -330,6 +330,7 @@ bool GetUserAgentMetrics(NPP npp) {
// The Chrome user_agent string also contains Safari. Search for Chrome first.
if (std::string::npos != user_agent.find("Chrome")) {
o3d::metric_browser_type = o3d::BROWSER_NAME_CHROME;
+ gIsChrome = true;
// The OmniWeb user_agent also contains Safari. Search for OminWeb first.
} else if (std::string::npos != user_agent.find("OmniWeb")) {
o3d::metric_browser_type = o3d::BROWSER_NAME_OMNIWEB;
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
diff --git a/o3d/plugin/mac/plugin_mac.h b/o3d/plugin/mac/plugin_mac.h
index 2d9d07a..7f3faa0 100644
--- a/o3d/plugin/mac/plugin_mac.h
+++ b/o3d/plugin/mac/plugin_mac.h
@@ -68,6 +68,7 @@ class RenderTimer {
};
extern RenderTimer gRenderTimer;
+extern bool gIsChrome;
void InitializeBreakpad();
void ShutdownBreakpad();
diff --git a/o3d/plugin/mac/plugin_mac.mm b/o3d/plugin/mac/plugin_mac.mm
index a9b42d7..96669ef 100644
--- a/o3d/plugin/mac/plugin_mac.mm
+++ b/o3d/plugin/mac/plugin_mac.mm
@@ -54,6 +54,8 @@ using o3d::DisplayWindowMac;
namespace o3d {
+bool gIsChrome = false;
+
// Returns the version number of the running Mac browser, as parsed from
// the short version string in the plist of the app's bundle.
bool GetBrowserVersionInfo(int *returned_major,
@@ -245,13 +247,17 @@ void RenderTimer::TimerCallback(CFRunLoopTimerRef timer, void* info) {
}
#endif
- // We're visible if (a) we are in fullscreen mode or (b) our cliprect
- // height and width are both a sensible size, ie > 1 pixel.
+ // We're visible if (a) we are in fullscreen mode, (b) our cliprect
+ // height and width are both a sensible size, ie > 1 pixel, or (c) if
+ // we are rendering to render surfaces (CoreGraphics drawing model,
+ // essentially offscreen rendering).
+ //
// We don't check for 0 as we have to size to 1 x 1 on occasion rather than
// 0 x 0 to avoid crashing the Apple software renderer, but do not want to
// actually draw to a 1 x 1 pixel area.
bool plugin_visible = in_fullscreen ||
- (obj->last_buffer_rect_[2] > 1 && obj->last_buffer_rect_[3] > 1);
+ (obj->last_buffer_rect_[2] > 1 && obj->last_buffer_rect_[3] > 1) ||
+ obj->IsOffscreenRenderingEnabled();
if (plugin_visible && obj->renderer()) {
if (obj->client()->render_mode() == o3d::Client::RENDERMODE_CONTINUOUS ||
@@ -265,7 +271,14 @@ void RenderTimer::TimerCallback(CFRunLoopTimerRef timer, void* info) {
aglSetInteger(obj->mac_agl_context_, AGL_SWAP_INTERVAL, &sync);
}
- obj->client()->RenderClient(true);
+ if (obj->IsOffscreenRenderingEnabled()) {
+ NPRect rect = { 0 };
+ rect.bottom = obj->width();
+ rect.right = obj->height();
+ NPN_InvalidateRect(instance, &rect);
+ } else {
+ obj->client()->RenderClient(true);
+ }
}
}
}
diff --git a/o3d/plugin/plugin.gyp b/o3d/plugin/plugin.gyp
index 8fdb21f..efe1495 100644
--- a/o3d/plugin/plugin.gyp
+++ b/o3d/plugin/plugin.gyp
@@ -58,8 +58,6 @@
],
# A comma-separated list of strings, each double-quoted.
'plugin_domain_whitelist%': '',
- # Whether to enable the English-only, Win/Mac-only fullscreen message.
- 'plugin_enable_fullscreen_msg%': '1',
},
'includes': [
'../build/common.gypi',
@@ -91,13 +89,6 @@
],
},
],
- ['<(plugin_enable_fullscreen_msg) != 0',
- {
- 'defines': [
- 'O3D_PLUGIN_ENABLE_FULLSCREEN_MSG=1',
- ],
- },
- ],
],
},
'targets': [