diff options
-rw-r--r-- | o3d/build/common.gypi | 10 | ||||
-rw-r--r-- | o3d/core/cross/client.cc | 27 | ||||
-rw-r--r-- | o3d/core/cross/client.h | 12 | ||||
-rw-r--r-- | o3d/core/cross/gl/render_surface_gl.cc | 12 | ||||
-rw-r--r-- | o3d/core/cross/gl/render_surface_gl.h | 4 | ||||
-rw-r--r-- | o3d/core/cross/gl/renderer_gl.cc | 3 | ||||
-rw-r--r-- | o3d/core/cross/gl/texture_gl.cc | 1 | ||||
-rw-r--r-- | o3d/core/cross/param_test.cc | 2 | ||||
-rw-r--r-- | o3d/core/cross/render_surface.cc | 24 | ||||
-rw-r--r-- | o3d/core/cross/render_surface.h | 11 | ||||
-rw-r--r-- | o3d/core/win/d3d9/render_surface_d3d9.cc | 26 | ||||
-rw-r--r-- | o3d/core/win/d3d9/render_surface_d3d9.h | 4 | ||||
-rw-r--r-- | o3d/plugin/cross/o3d_glue.cc | 104 | ||||
-rw-r--r-- | o3d/plugin/cross/o3d_glue.h | 37 | ||||
-rw-r--r-- | o3d/plugin/mac/config_mac.mm | 1 | ||||
-rw-r--r-- | o3d/plugin/mac/main_mac.mm | 186 | ||||
-rw-r--r-- | o3d/plugin/mac/plugin_mac.h | 1 | ||||
-rw-r--r-- | o3d/plugin/mac/plugin_mac.mm | 21 | ||||
-rw-r--r-- | o3d/plugin/plugin.gyp | 9 |
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': [ |