summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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': [