From d0a45cfbbbabc6d0054755d66b13f2cb1e8f2d49 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Thu, 1 Jan 2015 16:51:56 +0000 Subject: [PATCH] Add support for begin frame scheduling and direct rendering when GPU compositing is disabled (issue #1368). - Always set the browser process VSync rate (frame rate) to CefSettings.windowless_frame_rate. - When the `enable-begin-frame-scheduling` command-line flag is specified the VSync rate for all processes will be synchronized to CefSettings.windowless_frame_rate. This flag cannot be used in combination with windowed rendering. - When the `disable-gpu` and `disable-gpu-compositing` command-line flags are specified the CefRenderHandler::OnPaint method will be called directly from the compositor instead of requiring an additional copy for each frame. - CefRenderHandler::OnPopupSize now passes view coordinates instead of (potentially scaled) pixel coordinates. - Add OSR unit tests for 2x (HiDPI) pixel scaling. - Improve CefRenderHandler documentation. git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1960 5089003a-bbd8-11dd-ad1f-f1f9622dbc98 --- cef.gyp | 2 + include/capi/cef_render_handler_capi.h | 20 +- include/cef_render_handler.h | 16 +- libcef/browser/render_widget_host_view_osr.cc | 822 +++++++++++------- libcef/browser/render_widget_host_view_osr.h | 77 +- libcef/browser/software_output_device_osr.cc | 123 +++ libcef/browser/software_output_device_osr.h | 56 ++ patch/patch.cfg | 6 + patch/patches/compositor_1368.patch | 67 ++ tests/cefclient/cefclient_osr_widget_mac.mm | 34 +- tests/cefclient/osrenderer.h | 1 + tests/unittests/os_rendering_unittest.cc | 249 ++++-- 12 files changed, 1016 insertions(+), 457 deletions(-) create mode 100644 libcef/browser/software_output_device_osr.cc create mode 100644 libcef/browser/software_output_device_osr.h create mode 100644 patch/patches/compositor_1368.patch diff --git a/cef.gyp b/cef.gyp index 02700ba93..39086d124 100644 --- a/cef.gyp +++ b/cef.gyp @@ -984,6 +984,8 @@ 'libcef/browser/scheme_handler.h', 'libcef/browser/scheme_impl.cc', 'libcef/browser/scheme_impl.h', + 'libcef/browser/software_output_device_osr.cc', + 'libcef/browser/software_output_device_osr.h', 'libcef/browser/speech_recognition_manager_delegate.cc', 'libcef/browser/speech_recognition_manager_delegate.h', 'libcef/browser/stream_impl.cc', diff --git a/include/capi/cef_render_handler_capi.h b/include/capi/cef_render_handler_capi.h index 6245f9dbd..de99659d8 100644 --- a/include/capi/cef_render_handler_capi.h +++ b/include/capi/cef_render_handler_capi.h @@ -100,17 +100,20 @@ typedef struct _cef_render_handler_t { /// // Called when the browser wants to move or resize the popup widget. |rect| - // contains the new location and size. + // contains the new location and size in view coordinates. /// void (CEF_CALLBACK *on_popup_size)(struct _cef_render_handler_t* self, struct _cef_browser_t* browser, const cef_rect_t* rect); /// - // Called when an element should be painted. |type| indicates whether the - // element is the view or the popup widget. |buffer| contains the pixel data - // for the whole image. |dirtyRects| contains the set of rectangles that need - // to be repainted. |buffer| will be |width|*|height|*4 bytes in size and - // represents a BGRA image with an upper-left origin. + // Called when an element should be painted. Pixel values passed to this + // function are scaled relative to view coordinates based on the value of + // CefScreenInfo.device_scale_factor returned from GetScreenInfo. |type| + // indicates whether the element is the view or the popup widget. |buffer| + // contains the pixel data for the whole image. |dirtyRects| contains the set + // of rectangles in pixel coordinates that need to be repainted. |buffer| will + // be |width|*|height|*4 bytes in size and represents a BGRA image with an + // upper-left origin. /// void (CEF_CALLBACK *on_paint)(struct _cef_render_handler_t* self, struct _cef_browser_t* browser, cef_paint_element_type_t type, @@ -128,8 +131,9 @@ typedef struct _cef_render_handler_t { /// // Called when the user starts dragging content in the web view. Contextual - // information about the dragged content is supplied by |drag_data|. OS APIs - // that run a system message loop may be used within the StartDragging call. + // information about the dragged content is supplied by |drag_data|. (|x|, + // |y|) is the drag start location in screen coordinates. OS APIs that run a + // system message loop may be used within the StartDragging call. // // Return false (0) to abort the drag operation. Don't call any of // cef_browser_host_t::DragSource*Ended* functions after returning false (0). diff --git a/include/cef_render_handler.h b/include/cef_render_handler.h index 812c00a29..66de6822c 100644 --- a/include/cef_render_handler.h +++ b/include/cef_render_handler.h @@ -105,18 +105,21 @@ class CefRenderHandler : public virtual CefBase { /// // Called when the browser wants to move or resize the popup widget. |rect| - // contains the new location and size. + // contains the new location and size in view coordinates. /// /*--cef()--*/ virtual void OnPopupSize(CefRefPtr browser, const CefRect& rect) {} /// - // Called when an element should be painted. |type| indicates whether the - // element is the view or the popup widget. |buffer| contains the pixel data - // for the whole image. |dirtyRects| contains the set of rectangles that need - // to be repainted. |buffer| will be |width|*|height|*4 bytes in size and - // represents a BGRA image with an upper-left origin. + // Called when an element should be painted. Pixel values passed to this + // method are scaled relative to view coordinates based on the value of + // CefScreenInfo.device_scale_factor returned from GetScreenInfo. |type| + // indicates whether the element is the view or the popup widget. |buffer| + // contains the pixel data for the whole image. |dirtyRects| contains the set + // of rectangles in pixel coordinates that need to be repainted. |buffer| will + // be |width|*|height|*4 bytes in size and represents a BGRA image with an + // upper-left origin. /// /*--cef()--*/ virtual void OnPaint(CefRefPtr browser, @@ -138,6 +141,7 @@ class CefRenderHandler : public virtual CefBase { /// // Called when the user starts dragging content in the web view. Contextual // information about the dragged content is supplied by |drag_data|. + // (|x|, |y|) is the drag start location in screen coordinates. // OS APIs that run a system message loop may be used within the // StartDragging call. // diff --git a/libcef/browser/render_widget_host_view_osr.cc b/libcef/browser/render_widget_host_view_osr.cc index 2c10644d2..2a1f25c5e 100644 --- a/libcef/browser/render_widget_host_view_osr.cc +++ b/libcef/browser/render_widget_host_view_osr.cc @@ -5,18 +5,24 @@ #include "libcef/browser/render_widget_host_view_osr.h" #include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/software_output_device_osr.h" #include "libcef/browser/thread_util.h" #include "base/callback_helpers.h" +#include "base/command_line.h" #include "cc/output/copy_output_request.h" +#include "cc/scheduler/delay_based_time_source.h" #include "content/browser/compositor/image_transport_factory.h" #include "content/browser/compositor/resize_lock.h" #include "content/browser/renderer_host/dip_util.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/common/gpu/client/gl_helper.h" +#include "content/common/view_messages.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/context_factory.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host_view_frame_subscriber.h" +#include "content/public/common/content_switches.h" #include "third_party/WebKit/public/platform/WebScreenInfo.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/geometry/size_conversions.h" @@ -114,14 +120,316 @@ class CefResizeLock : public content::ResizeLock { } // namespace -CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR( - content::RenderWidgetHost* widget) - : delegated_frame_host_(new content::DelegatedFrameHost(this)), - compositor_widget_(gfx::kNullAcceleratedWidget), - frame_rate_threshold_ms_(1000 / kDefaultFrameRate), +// Used for managing copy requests when GPU compositing is enabled. Based on +// RendererOverridesHandler::InnerSwapCompositorFrame and +// DelegatedFrameHost::CopyFromCompositingSurface. +class CefCopyFrameGenerator { + public: + CefCopyFrameGenerator(int frame_rate_threshold_ms, + CefRenderWidgetHostViewOSR* view) + : frame_rate_threshold_ms_(frame_rate_threshold_ms), + view_(view), frame_pending_(false), frame_in_progress_(false), frame_retry_count_(0), + weak_ptr_factory_(this) { + } + + void GenerateCopyFrame( + bool force_frame, + const gfx::Rect& damage_rect) { + if (force_frame && !frame_pending_) + frame_pending_ = true; + + // No frame needs to be generated at this time. + if (!frame_pending_) + return; + + // Keep track of |damage_rect| for when the next frame is generated. + if (!damage_rect.IsEmpty()) + pending_damage_rect_.Union(damage_rect); + + // Don't attempt to generate a frame while one is currently in-progress. + if (frame_in_progress_) + return; + frame_in_progress_ = true; + + // Don't exceed the frame rate threshold. + const int64 frame_rate_delta = + (base::TimeTicks::Now() - frame_start_time_).InMilliseconds(); + if (frame_rate_delta < frame_rate_threshold_ms_) { + // Generate the frame after the necessary time has passed. + CEF_POST_DELAYED_TASK(CEF_UIT, + base::Bind(&CefCopyFrameGenerator::InternalGenerateCopyFrame, + weak_ptr_factory_.GetWeakPtr()), + frame_rate_threshold_ms_ - frame_rate_delta); + return; + } + + InternalGenerateCopyFrame(); + } + + bool frame_pending() const { return frame_pending_; } + + private: + void InternalGenerateCopyFrame() { + frame_pending_ = false; + frame_start_time_ = base::TimeTicks::Now(); + + if (!view_->render_widget_host()) + return; + + const gfx::Rect damage_rect = pending_damage_rect_; + pending_damage_rect_.SetRect(0, 0, 0, 0); + + // The below code is similar in functionality to + // DelegatedFrameHost::CopyFromCompositingSurface but we reuse the same + // SkBitmap in the GPU codepath and avoid scaling where possible. + scoped_ptr request = + cc::CopyOutputRequest::CreateRequest(base::Bind( + &CefCopyFrameGenerator::CopyFromCompositingSurfaceHasResult, + weak_ptr_factory_.GetWeakPtr(), + damage_rect)); + + request->set_area(gfx::Rect(view_->GetPhysicalBackingSize())); + view_->RequestCopyOfOutput(request.Pass()); + } + + void CopyFromCompositingSurfaceHasResult( + const gfx::Rect& damage_rect, + scoped_ptr result) { + if (result->IsEmpty() || result->size().IsEmpty() || + !view_->render_widget_host()) { + OnCopyFrameCaptureFailure(damage_rect); + return; + } + + if (result->HasTexture()) { + PrepareTextureCopyOutputResult(damage_rect, result.Pass()); + return; + } + + DCHECK(result->HasBitmap()); + PrepareBitmapCopyOutputResult(damage_rect, result.Pass()); + } + + void PrepareTextureCopyOutputResult( + const gfx::Rect& damage_rect, + scoped_ptr result) { + DCHECK(result->HasTexture()); + base::ScopedClosureRunner scoped_callback_runner( + base::Bind(&CefCopyFrameGenerator::OnCopyFrameCaptureFailure, + weak_ptr_factory_.GetWeakPtr(), + damage_rect)); + + const gfx::Size& result_size = result->size(); + SkIRect bitmap_size; + if (bitmap_) + bitmap_->getBounds(&bitmap_size); + + if (!bitmap_ || + bitmap_size.width() != result_size.width() || + bitmap_size.height() != result_size.height()) { + // Create a new bitmap if the size has changed. + bitmap_.reset(new SkBitmap); + bitmap_->allocN32Pixels(result_size.width(), + result_size.height(), + true); + if (bitmap_->drawsNothing()) + return; + } + + content::ImageTransportFactory* factory = + content::ImageTransportFactory::GetInstance(); + content::GLHelper* gl_helper = factory->GetGLHelper(); + if (!gl_helper) + return; + + scoped_ptr bitmap_pixels_lock( + new SkAutoLockPixels(*bitmap_)); + uint8* pixels = static_cast(bitmap_->getPixels()); + + cc::TextureMailbox texture_mailbox; + scoped_ptr release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) + return; + + ignore_result(scoped_callback_runner.Release()); + + gl_helper->CropScaleReadbackAndCleanMailbox( + texture_mailbox.mailbox(), + texture_mailbox.sync_point(), + result_size, + gfx::Rect(result_size), + result_size, + pixels, + kN32_SkColorType, + base::Bind( + &CefCopyFrameGenerator::CopyFromCompositingSurfaceFinishedProxy, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(&release_callback), + damage_rect, + base::Passed(&bitmap_), + base::Passed(&bitmap_pixels_lock)), + content::GLHelper::SCALER_QUALITY_FAST); + } + + static void CopyFromCompositingSurfaceFinishedProxy( + base::WeakPtr generator, + scoped_ptr release_callback, + const gfx::Rect& damage_rect, + scoped_ptr bitmap, + scoped_ptr bitmap_pixels_lock, + bool result) { + // This method may be called after the view has been deleted. + uint32 sync_point = 0; + if (result) { + content::GLHelper* gl_helper = + content::ImageTransportFactory::GetInstance()->GetGLHelper(); + sync_point = gl_helper->InsertSyncPoint(); + } + bool lost_resource = sync_point == 0; + release_callback->Run(sync_point, lost_resource); + + if (generator) { + generator->CopyFromCompositingSurfaceFinished( + damage_rect, bitmap.Pass(), bitmap_pixels_lock.Pass(), result); + } else { + bitmap_pixels_lock.reset(); + bitmap.reset(); + } + } + + void CopyFromCompositingSurfaceFinished( + const gfx::Rect& damage_rect, + scoped_ptr bitmap, + scoped_ptr bitmap_pixels_lock, + bool result) { + // Restore ownership of the bitmap to the view. + DCHECK(!bitmap_); + bitmap_ = bitmap.Pass(); + + if (result) { + OnCopyFrameCaptureSuccess(damage_rect, *bitmap_, + bitmap_pixels_lock.Pass()); + } else { + bitmap_pixels_lock.reset(); + OnCopyFrameCaptureFailure(damage_rect); + } + } + + void PrepareBitmapCopyOutputResult( + const gfx::Rect& damage_rect, + scoped_ptr result) { + DCHECK(result->HasBitmap()); + scoped_ptr source = result->TakeBitmap(); + DCHECK(source); + if (source) { + scoped_ptr bitmap_pixels_lock( + new SkAutoLockPixels(*source)); + OnCopyFrameCaptureSuccess(damage_rect, *source, + bitmap_pixels_lock.Pass()); + } else { + OnCopyFrameCaptureFailure(damage_rect); + } + } + + void OnCopyFrameCaptureFailure( + const gfx::Rect& damage_rect) { + // Retry with the same |damage_rect|. + pending_damage_rect_.Union(damage_rect); + + const bool force_frame = (++frame_retry_count_ <= kFrameRetryLimit); + OnCopyFrameCaptureCompletion(force_frame); + } + + void OnCopyFrameCaptureSuccess( + const gfx::Rect& damage_rect, + const SkBitmap& bitmap, + scoped_ptr bitmap_pixels_lock) { + view_->OnPaint(damage_rect, bitmap.width(), bitmap.height(), + bitmap.getPixels()); + bitmap_pixels_lock.reset(); + + // Reset the frame retry count on successful frame generation. + if (frame_retry_count_ > 0) + frame_retry_count_ = 0; + + OnCopyFrameCaptureCompletion(false); + } + + void OnCopyFrameCaptureCompletion(bool force_frame) { + frame_in_progress_ = false; + + if (frame_pending_) { + // Another frame was requested while the current frame was in-progress. + // Generate the pending frame now. + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefCopyFrameGenerator::GenerateCopyFrame, + weak_ptr_factory_.GetWeakPtr(), + force_frame, + gfx::Rect())); + } + } + + const int frame_rate_threshold_ms_; + CefRenderWidgetHostViewOSR* view_; + + base::TimeTicks frame_start_time_; + bool frame_pending_; + bool frame_in_progress_; + int frame_retry_count_; + scoped_ptr bitmap_; + gfx::Rect pending_damage_rect_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CefCopyFrameGenerator); +}; + +// Used to control the VSync rate in subprocesses when BeginFrame scheduling is +// enabled. +class CefBeginFrameTimer : public cc::TimeSourceClient { + public: + CefBeginFrameTimer(int frame_rate_threshold_ms, + const base::Closure& callback) + : callback_(callback) { + time_source_ = cc::DelayBasedTimeSource::Create( + base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms), + content::BrowserThread::GetMessageLoopProxyForThread(CEF_UIT).get()); + time_source_->SetClient(this); + } + + void SetActive(bool active) { + time_source_->SetActive(active); + } + + bool IsActive() const { + return time_source_->Active(); + } + + private: + // cc::TimerSourceClient implementation. + void OnTimerTick() override { + callback_.Run(); + } + + const base::Closure callback_; + scoped_refptr time_source_; + + DISALLOW_COPY_AND_ASSIGN(CefBeginFrameTimer); +}; + + +CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR( + content::RenderWidgetHost* widget) + : scale_factor_(kDefaultScaleFactor), + frame_rate_threshold_ms_(0), + delegated_frame_host_(new content::DelegatedFrameHost(this)), + compositor_widget_(gfx::kNullAcceleratedWidget), + software_output_device_(NULL), hold_resize_(false), pending_resize_(false), render_widget_host_(content::RenderWidgetHostImpl::From(widget)), @@ -153,6 +461,7 @@ CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR( content::GetContextFactory(), base::MessageLoopProxy::current())); #endif + compositor_->SetDelegate(this); compositor_->SetRootLayer(root_layer_.get()); if (browser_impl_.get()) @@ -168,6 +477,9 @@ CefRenderWidgetHostViewOSR::~CefRenderWidgetHostViewOSR() { PlatformDestroyCompositorWidget(); + if (copy_frame_generator_.get()) + copy_frame_generator_.reset(NULL); + delegated_frame_host_.reset(NULL); compositor_.reset(NULL); root_layer_.reset(NULL); @@ -275,21 +587,46 @@ void CefRenderWidgetHostViewOSR::OnSwapCompositorFrame( } if (frame->delegated_frame_data) { - // Determine the damage rectangle for the current frame. This is the same - // calculation that SwapDelegatedFrame uses. - cc::RenderPass* root_pass = - frame->delegated_frame_data->render_pass_list.back(); - gfx::Size frame_size = root_pass->output_rect.size(); - gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect); - damage_rect.Intersect(gfx::Rect(frame_size)); + if (software_output_device_) { + if (!begin_frame_timer_.get()) { + // If BeginFrame scheduling is enabled SoftwareOutputDevice activity + // will be controlled via OnSetNeedsBeginFrames. Otherwise, activate + // the SoftwareOutputDevice now (when the first frame is generated). + software_output_device_->SetActive(true); + } - delegated_frame_host_->SwapDelegatedFrame( - output_surface_id, - frame->delegated_frame_data.Pass(), - frame->metadata.device_scale_factor, - frame->metadata.latency_info); + // The compositor will draw directly to the SoftwareOutputDevice which + // then calls OnPaint. + delegated_frame_host_->SwapDelegatedFrame( + output_surface_id, + frame->delegated_frame_data.Pass(), + frame->metadata.device_scale_factor, + frame->metadata.latency_info); + } else { + if (!copy_frame_generator_.get()) { + copy_frame_generator_.reset( + new CefCopyFrameGenerator(frame_rate_threshold_ms_, this)); + } + + // Determine the damage rectangle for the current frame. This is the same + // calculation that SwapDelegatedFrame uses. + cc::RenderPass* root_pass = + frame->delegated_frame_data->render_pass_list.back(); + gfx::Size frame_size = root_pass->output_rect.size(); + gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect); + damage_rect.Intersect(gfx::Rect(frame_size)); + + delegated_frame_host_->SwapDelegatedFrame( + output_surface_id, + frame->delegated_frame_data.Pass(), + frame->metadata.device_scale_factor, + frame->metadata.latency_info); + + // Request a copy of the last compositor frame which will eventually call + // OnPaint asynchronously. + copy_frame_generator_->GenerateCopyFrame(true, damage_rect); + } - GenerateFrame(true, damage_rect); return; } @@ -307,8 +644,7 @@ void CefRenderWidgetHostViewOSR::InitAsPopup( parent_host_view_ = static_cast( parent_host_view); browser_impl_ = parent_host_view_->browser_impl(); - if (!browser_impl_.get()) - return; + DCHECK(browser_impl_.get()); if (parent_host_view_->popup_host_view_) { // Cancel the previous popup widget. @@ -321,12 +657,7 @@ void CefRenderWidgetHostViewOSR::InitAsPopup( popup_position_ = pos; - const float scale_factor = CurrentDeviceScaleFactor(); - const gfx::Rect scaled_size = - gfx::ToNearestRect(gfx::ScaleRect(pos, scale_factor)); - - CefRect widget_pos(scaled_size.x(), scaled_size.y(), - scaled_size.width(), scaled_size.height()); + CefRect widget_pos(pos.x(), pos.y(), pos.width(), pos.height()); browser_impl_->GetClient()->GetRenderHandler()->OnPopupSize( browser_impl_.get(), widget_pos); @@ -482,10 +813,7 @@ gfx::Size CefRenderWidgetHostViewOSR::GetRequestedRendererSize() const { } gfx::Size CefRenderWidgetHostViewOSR::GetPhysicalBackingSize() const { - float scale_factor = const_cast(this)-> - CurrentDeviceScaleFactor(); - return gfx::ToCeiledSize(gfx::ScaleSize(GetRequestedRendererSize(), - scale_factor)); + return gfx::ConvertSizeToPixel(scale_factor_, GetRequestedRendererSize()); } void CefRenderWidgetHostViewOSR::SelectionBoundsChanged( @@ -609,6 +937,44 @@ void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged( } #endif +bool CefRenderWidgetHostViewOSR::OnMessageReceived(const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(CefRenderWidgetHostViewOSR, msg) + IPC_MESSAGE_HANDLER(ViewHostMsg_SetNeedsBeginFrames, + OnSetNeedsBeginFrames) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + if (!handled) + return content::RenderWidgetHostViewBase::OnMessageReceived(msg); + return handled; +} + +void CefRenderWidgetHostViewOSR::OnSetNeedsBeginFrames(bool enabled) { + // Start/stop the timer that sends BeginFrame requests. + begin_frame_timer_->SetActive(enabled); + if (software_output_device_) { + // When the SoftwareOutputDevice is active it will call OnPaint for each + // frame. If the SoftwareOutputDevice is deactivated while an invalidation + // region is pending it will call OnPaint immediately. + software_output_device_->SetActive(enabled); + } +} + +scoped_ptr +CefRenderWidgetHostViewOSR::CreateSoftwareOutputDevice( + ui::Compositor* compositor) { + DCHECK_EQ(compositor_.get(), compositor); + DCHECK(!copy_frame_generator_); + DCHECK(!software_output_device_); + software_output_device_ = new CefSoftwareOutputDeviceOSR( + compositor, + browser_impl_.get() ? browser_impl_->IsTransparent() : false, + base::Bind(&CefRenderWidgetHostViewOSR::OnPaint, + weak_ptr_factory_.GetWeakPtr())); + return make_scoped_ptr(software_output_device_); +} + ui::Compositor* CefRenderWidgetHostViewOSR::GetCompositor() const { return compositor_.get(); } @@ -641,18 +1007,7 @@ gfx::Size CefRenderWidgetHostViewOSR::DesiredFrameSize() { } float CefRenderWidgetHostViewOSR::CurrentDeviceScaleFactor() { - if (!browser_impl_.get()) - return kDefaultScaleFactor; - - CefScreenInfo screen_info( - kDefaultScaleFactor, 0, 0, false, CefRect(), CefRect()); - if (!browser_impl_->GetClient()->GetRenderHandler()->GetScreenInfo( - browser_impl_.get(), screen_info)) { - // Use the default - return kDefaultScaleFactor; - } - - return screen_info.device_scale_factor; + return scale_factor_; } gfx::Size CefRenderWidgetHostViewOSR::ConvertViewSizeToPixel( @@ -668,6 +1023,7 @@ content::DelegatedFrameHost* bool CefRenderWidgetHostViewOSR::InstallTransparency() { if (browser_impl_.get() && browser_impl_->IsTransparent()) { SetBackgroundColor(SkColorSetARGB(SK_AlphaTRANSPARENT, 0, 0, 0)); + compositor_->SetHostHasTransparentBackground(true); return true; } return false; @@ -709,7 +1065,19 @@ void CefRenderWidgetHostViewOSR::Invalidate( return; } - GenerateFrame(true, root_layer_->bounds()); + const gfx::Rect& bounds_in_pixels = gfx::Rect(GetPhysicalBackingSize()); + + if (software_output_device_) { + if (IsFramePending()) { + // Include the invalidated region in the next frame generated. + software_output_device_->Invalidate(bounds_in_pixels); + } else { + // Call OnPaint immediately. + software_output_device_->OnPaint(bounds_in_pixels); + } + } else if (copy_frame_generator_.get()) { + copy_frame_generator_->GenerateCopyFrame(true, bounds_in_pixels); + } } void CefRenderWidgetHostViewOSR::SendKeyEvent( @@ -811,246 +1179,17 @@ void CefRenderWidgetHostViewOSR::ReleaseResize() { } } -void CefRenderWidgetHostViewOSR::SetFrameRate() { - if (!browser_impl_.get()) - return; - int frame_rate = browser_impl_->settings().windowless_frame_rate; - if (frame_rate < 1) - frame_rate = kDefaultFrameRate; - else if (frame_rate > kMaximumFrameRate) - frame_rate = kMaximumFrameRate; - frame_rate_threshold_ms_ = 1000 / frame_rate; -} - -void CefRenderWidgetHostViewOSR::ResizeRootLayer() { - SetFrameRate(); - - gfx::Size size; - if (!IsPopupWidget()) - size = GetViewBounds().size(); - else - size = popup_position_.size(); - - if (size == root_layer_->bounds().size()) - return; - - root_layer_->SetBounds(gfx::Rect(0, 0, size.width(), size.height())); - compositor_->SetScaleAndSize(CurrentDeviceScaleFactor(), size); -} - -void CefRenderWidgetHostViewOSR::GenerateFrame( - bool force_frame, - const gfx::Rect& damage_rect) { - if (force_frame && !frame_pending_) - frame_pending_ = true; - - // No frame needs to be generated at this time. - if (!frame_pending_) - return; - - // Keep track of |damage_rect| for when the next frame is generated. - if (!damage_rect.IsEmpty()) - pending_damage_rect_.Union(damage_rect); - - // Don't attempt to generate a frame while one is currently in-progress. - if (frame_in_progress_) - return; - frame_in_progress_ = true; - - // Don't exceed the frame rate threshold. - const int64 frame_rate_delta = - (base::TimeTicks::Now() - frame_start_time_).InMilliseconds(); - if (frame_rate_delta < frame_rate_threshold_ms_) { - // Generate the frame after the necessary time has passed. - CEF_POST_DELAYED_TASK(CEF_UIT, - base::Bind(&CefRenderWidgetHostViewOSR::InternalGenerateFrame, - weak_ptr_factory_.GetWeakPtr()), - frame_rate_threshold_ms_ - frame_rate_delta); - return; - } - - InternalGenerateFrame(); -} - -void CefRenderWidgetHostViewOSR::InternalGenerateFrame() { - frame_pending_ = false; - frame_start_time_ = base::TimeTicks::Now(); - - if (!render_widget_host_) - return; - - const gfx::Rect damage_rect = pending_damage_rect_; - pending_damage_rect_.SetRect(0, 0, 0, 0); - - // The below code is similar in functionality to - // DelegatedFrameHost::CopyFromCompositingSurface but we reuse the same - // SkBitmap in the GPU codepath and avoid scaling where possible. - scoped_ptr request = - cc::CopyOutputRequest::CreateRequest(base::Bind( - &CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceHasResult, - weak_ptr_factory_.GetWeakPtr(), - damage_rect)); - - const gfx::Rect& src_subrect_in_pixel = - gfx::ConvertRectToPixel(CurrentDeviceScaleFactor(), - root_layer_->bounds()); - request->set_area(src_subrect_in_pixel); - RequestCopyOfOutput(request.Pass()); -} - -void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceHasResult( +void CefRenderWidgetHostViewOSR::OnPaint( const gfx::Rect& damage_rect, - scoped_ptr result) { - if (result->IsEmpty() || result->size().IsEmpty() || !render_widget_host_) { - OnFrameCaptureFailure(damage_rect); - return; - } + int bitmap_width, + int bitmap_height, + void* bitmap_pixels) { + TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::OnPaint"); - if (result->HasTexture()) { - PrepareTextureCopyOutputResult(damage_rect, result.Pass()); - return; - } + // Don't execute WasResized while the OnPaint callback is pending. + HoldResize(); - DCHECK(result->HasBitmap()); - PrepareBitmapCopyOutputResult(damage_rect, result.Pass()); -} - -void CefRenderWidgetHostViewOSR::PrepareTextureCopyOutputResult( - const gfx::Rect& damage_rect, - scoped_ptr result) { - DCHECK(result->HasTexture()); - base::ScopedClosureRunner scoped_callback_runner( - base::Bind(&CefRenderWidgetHostViewOSR::OnFrameCaptureFailure, - weak_ptr_factory_.GetWeakPtr(), - damage_rect)); - - const gfx::Size& result_size = result->size(); - SkIRect bitmap_size; - if (bitmap_) - bitmap_->getBounds(&bitmap_size); - - if (!bitmap_ || - bitmap_size.width() != result_size.width() || - bitmap_size.height() != result_size.height()) { - // Create a new bitmap if the size has changed. - bitmap_.reset(new SkBitmap); - bitmap_->allocN32Pixels(result_size.width(), - result_size.height(), - true); - if (bitmap_->drawsNothing()) - return; - } - - content::ImageTransportFactory* factory = - content::ImageTransportFactory::GetInstance(); - content::GLHelper* gl_helper = factory->GetGLHelper(); - if (!gl_helper) - return; - - scoped_ptr bitmap_pixels_lock( - new SkAutoLockPixels(*bitmap_)); - uint8* pixels = static_cast(bitmap_->getPixels()); - - cc::TextureMailbox texture_mailbox; - scoped_ptr release_callback; - result->TakeTexture(&texture_mailbox, &release_callback); - DCHECK(texture_mailbox.IsTexture()); - if (!texture_mailbox.IsTexture()) - return; - - ignore_result(scoped_callback_runner.Release()); - - gl_helper->CropScaleReadbackAndCleanMailbox( - texture_mailbox.mailbox(), - texture_mailbox.sync_point(), - result_size, - gfx::Rect(result_size), - result_size, - pixels, - kN32_SkColorType, - base::Bind( - &CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinishedProxy, - weak_ptr_factory_.GetWeakPtr(), - base::Passed(&release_callback), - damage_rect, - base::Passed(&bitmap_), - base::Passed(&bitmap_pixels_lock)), - content::GLHelper::SCALER_QUALITY_FAST); -} - -// static -void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinishedProxy( - base::WeakPtr view, - scoped_ptr release_callback, - const gfx::Rect& damage_rect, - scoped_ptr bitmap, - scoped_ptr bitmap_pixels_lock, - bool result) { - // This method may be called after the view has been deleted. - uint32 sync_point = 0; - if (result) { - content::GLHelper* gl_helper = - content::ImageTransportFactory::GetInstance()->GetGLHelper(); - sync_point = gl_helper->InsertSyncPoint(); - } - bool lost_resource = sync_point == 0; - release_callback->Run(sync_point, lost_resource); - - if (view) { - view->CopyFromCompositingSurfaceFinished( - damage_rect, bitmap.Pass(), bitmap_pixels_lock.Pass(), result); - } else { - bitmap_pixels_lock.reset(); - bitmap.reset(); - } -} - -void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinished( - const gfx::Rect& damage_rect, - scoped_ptr bitmap, - scoped_ptr bitmap_pixels_lock, - bool result) { - // Restore ownership of the bitmap to the view. - DCHECK(!bitmap_); - bitmap_ = bitmap.Pass(); - - if (result) { - OnFrameCaptureSuccess(damage_rect, *bitmap_, bitmap_pixels_lock.Pass()); - } else { - bitmap_pixels_lock.reset(); - OnFrameCaptureFailure(damage_rect); - } -} - -void CefRenderWidgetHostViewOSR::PrepareBitmapCopyOutputResult( - const gfx::Rect& damage_rect, - scoped_ptr result) { - DCHECK(result->HasBitmap()); - scoped_ptr source = result->TakeBitmap(); - DCHECK(source); - if (source) { - scoped_ptr bitmap_pixels_lock( - new SkAutoLockPixels(*source)); - OnFrameCaptureSuccess(damage_rect, *source, bitmap_pixels_lock.Pass()); - } else { - OnFrameCaptureFailure(damage_rect); - } -} - -void CefRenderWidgetHostViewOSR::OnFrameCaptureFailure( - const gfx::Rect& damage_rect) { - // Retry with the same |damage_rect|. - pending_damage_rect_.Union(damage_rect); - - const bool force_frame = (++frame_retry_count_ <= kFrameRetryLimit); - OnFrameCaptureCompletion(force_frame); -} - -void CefRenderWidgetHostViewOSR::OnFrameCaptureSuccess( - const gfx::Rect& damage_rect, - const SkBitmap& bitmap, - scoped_ptr bitmap_pixels_lock) { - gfx::Rect rect_in_bitmap(0, 0, bitmap.width(), bitmap.height()); + gfx::Rect rect_in_bitmap(0, 0, bitmap_width, bitmap_height); rect_in_bitmap.Intersect(damage_rect); CefRenderHandler::RectList rcList; @@ -1061,33 +1200,116 @@ void CefRenderWidgetHostViewOSR::OnFrameCaptureSuccess( browser_impl_.get(), IsPopupWidget() ? PET_POPUP : PET_VIEW, rcList, - bitmap.getPixels(), - bitmap.width(), - bitmap.height()); + bitmap_pixels, + bitmap_width, + bitmap_height); - bitmap_pixels_lock.reset(); - - // Reset the frame retry count on successful frame generation. - if (frame_retry_count_ > 0) - frame_retry_count_ = 0; - - OnFrameCaptureCompletion(false); + ReleaseResize(); } -void CefRenderWidgetHostViewOSR::OnFrameCaptureCompletion(bool force_frame) { - frame_in_progress_ = false; +void CefRenderWidgetHostViewOSR::SetFrameRate() { + DCHECK(browser_impl_.get()); + if (!browser_impl_.get()) + return; - if (frame_pending_) { - // Another frame was requested while the current frame was in-progress. - // Generate the pending frame now. - CEF_POST_TASK(CEF_UIT, - base::Bind(&CefRenderWidgetHostViewOSR::GenerateFrame, - weak_ptr_factory_.GetWeakPtr(), - force_frame, - gfx::Rect())); + // Only set the frame rate one time. + if (frame_rate_threshold_ms_ != 0) + return; + + int frame_rate = browser_impl_->settings().windowless_frame_rate; + if (frame_rate < 1) + frame_rate = kDefaultFrameRate; + else if (frame_rate > kMaximumFrameRate) + frame_rate = kMaximumFrameRate; + frame_rate_threshold_ms_ = 1000 / frame_rate; + + // Configure the VSync interval for the browser process. + compositor_->vsync_manager()->SetAuthoritativeVSyncInterval( + base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_)); + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kEnableBeginFrameScheduling)) { + DCHECK(!begin_frame_timer_.get()); + begin_frame_timer_.reset(new CefBeginFrameTimer( + frame_rate_threshold_ms_, + base::Bind(&CefRenderWidgetHostViewOSR::OnBeginFrameTimerTick, + weak_ptr_factory_.GetWeakPtr()))); } } +void CefRenderWidgetHostViewOSR::SetDeviceScaleFactor() { + if (browser_impl_.get()) { + CefScreenInfo screen_info( + kDefaultScaleFactor, 0, 0, false, CefRect(), CefRect()); + if (browser_impl_->GetClient()->GetRenderHandler()->GetScreenInfo( + browser_impl_.get(), screen_info)) { + scale_factor_ = screen_info.device_scale_factor; + return; + } + } + + scale_factor_ = kDefaultScaleFactor; +} + +void CefRenderWidgetHostViewOSR::ResizeRootLayer() { + SetFrameRate(); + SetDeviceScaleFactor(); + + gfx::Size size; + if (!IsPopupWidget()) + size = GetViewBounds().size(); + else + size = popup_position_.size(); + + if (size == root_layer_->bounds().size()) + return; + + const gfx::Size& size_in_pixels = + gfx::ConvertSizeToPixel(scale_factor_, size); + + root_layer_->SetBounds(gfx::Rect(size)); + compositor_->SetScaleAndSize(scale_factor_, size_in_pixels); +} + +bool CefRenderWidgetHostViewOSR::IsFramePending() { + if (!IsShowing()) + return false; + + if (begin_frame_timer_.get()) + return begin_frame_timer_->IsActive(); + else if (copy_frame_generator_.get()) + return copy_frame_generator_->frame_pending(); + + return false; +} + +void CefRenderWidgetHostViewOSR::OnBeginFrameTimerTick() { + const base::TimeTicks frame_time = base::TimeTicks::Now(); + const base::TimeDelta vsync_period = + base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_); + SendBeginFrame(frame_time, vsync_period); +} + +void CefRenderWidgetHostViewOSR::SendBeginFrame(base::TimeTicks frame_time, + base::TimeDelta vsync_period) { + TRACE_EVENT1("libcef", "CefRenderWidgetHostViewOSR::SendBeginFrame", + "frame_time_us", frame_time.ToInternalValue()); + + base::TimeTicks display_time = frame_time + vsync_period; + + // TODO(brianderson): Use adaptive draw-time estimation. + base::TimeDelta estimated_browser_composite_time = + base::TimeDelta::FromMicroseconds( + (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60)); + + base::TimeTicks deadline = display_time - estimated_browser_composite_time; + + render_widget_host_->Send(new ViewMsg_BeginFrame( + render_widget_host_->GetRoutingID(), + cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, + vsync_period, cc::BeginFrameArgs::NORMAL))); +} + void CefRenderWidgetHostViewOSR::CancelPopupWidget() { DCHECK(IsPopupWidget()); diff --git a/libcef/browser/render_widget_host_view_osr.h b/libcef/browser/render_widget_host_view_osr.h index b3c061008..b2e83d5ed 100644 --- a/libcef/browser/render_widget_host_view_osr.h +++ b/libcef/browser/render_widget_host_view_osr.h @@ -32,7 +32,10 @@ class RenderWidgetHostImpl; class BackingStore; } +class CefBeginFrameTimer; class CefBrowserHostImpl; +class CefCopyFrameGenerator; +class CefSoftwareOutputDeviceOSR; class CefWebContentsViewOSR; #if defined(OS_MACOSX) @@ -72,6 +75,7 @@ class CefRenderWidgetHostViewOSR #if defined(OS_MACOSX) public ui::AcceleratedWidgetMacNSView, #endif + public ui::CompositorDelegate, public content::DelegatedFrameHostClient { public: explicit CefRenderWidgetHostViewOSR(content::RenderWidgetHost* widget); @@ -195,6 +199,15 @@ class CefRenderWidgetHostViewOSR void AcceleratedWidgetHitError() override; #endif // defined(OS_MACOSX) + bool OnMessageReceived(const IPC::Message& msg) override; + + // Message handlers. + void OnSetNeedsBeginFrames(bool enabled); + + // ui::CompositorDelegate implementation. + scoped_ptr CreateSoftwareOutputDevice( + ui::Compositor* compositor) override; + // DelegatedFrameHostClient implementation. ui::Compositor* GetCompositor() const override; ui::Layer* GetLayer() override; @@ -220,6 +233,11 @@ class CefRenderWidgetHostViewOSR void HoldResize(); void ReleaseResize(); + void OnPaint(const gfx::Rect& damage_rect, + int bitmap_width, + int bitmap_height, + void* bitmap_pixels); + bool IsPopupWidget() const { return popup_type_ != blink::WebPopupTypeNone; } @@ -248,39 +266,16 @@ class CefRenderWidgetHostViewOSR private: void SetFrameRate(); + void SetDeviceScaleFactor(); void ResizeRootLayer(); - // Implementation based on RendererOverridesHandler::InnerSwapCompositorFrame - // and DelegatedFrameHost::CopyFromCompositingSurface. - void GenerateFrame(bool force_frame, const gfx::Rect& damage_rect); - void InternalGenerateFrame(); - void CopyFromCompositingSurfaceHasResult( - const gfx::Rect& damage_rect, - scoped_ptr result); - void PrepareTextureCopyOutputResult( - const gfx::Rect& damage_rect, - scoped_ptr result); - static void CopyFromCompositingSurfaceFinishedProxy( - base::WeakPtr view, - scoped_ptr release_callback, - const gfx::Rect& damage_rect, - scoped_ptr bitmap, - scoped_ptr bitmap_pixels_lock, - bool result); - void CopyFromCompositingSurfaceFinished( - const gfx::Rect& damage_rect, - scoped_ptr bitmap, - scoped_ptr bitmap_pixels_lock, - bool result); - void PrepareBitmapCopyOutputResult( - const gfx::Rect& damage_rect, - scoped_ptr result); - void OnFrameCaptureFailure(const gfx::Rect& damage_rect); - void OnFrameCaptureSuccess( - const gfx::Rect& damage_rect, - const SkBitmap& bitmap, - scoped_ptr bitmap_pixels_lock); - void OnFrameCaptureCompletion(bool force_frame); + // Returns a best guess whether a frame is currently pending. + bool IsFramePending(); + + // Called by CefBeginFrameTimer to send a BeginFrame request. + void OnBeginFrameTimerTick(); + void SendBeginFrame(base::TimeTicks frame_time, + base::TimeDelta vsync_period); void CancelPopupWidget(); @@ -311,6 +306,9 @@ class CefRenderWidgetHostViewOSR void PlatformCreateCompositorWidget(); void PlatformDestroyCompositorWidget(); + float scale_factor_; + int frame_rate_threshold_ms_; + scoped_ptr delegated_frame_host_; scoped_ptr compositor_; gfx::AcceleratedWidget compositor_widget_; @@ -326,13 +324,16 @@ class CefRenderWidgetHostViewOSR CefWindowX11* window_; #endif - int frame_rate_threshold_ms_; - base::TimeTicks frame_start_time_; - bool frame_pending_; - bool frame_in_progress_; - int frame_retry_count_; - scoped_ptr bitmap_; - gfx::Rect pending_damage_rect_; + // Used to control the VSync rate in subprocesses when BeginFrame scheduling + // is enabled. + scoped_ptr begin_frame_timer_; + + // Used for direct rendering from the compositor when GPU compositing is + // disabled. This object is owned by the compositor. + CefSoftwareOutputDeviceOSR* software_output_device_; + + // Used for managing copy requests when GPU compositing is enabled. + scoped_ptr copy_frame_generator_; bool hold_resize_; bool pending_resize_; diff --git a/libcef/browser/software_output_device_osr.cc b/libcef/browser/software_output_device_osr.cc new file mode 100644 index 000000000..65aaca7ed --- /dev/null +++ b/libcef/browser/software_output_device_osr.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2014 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libcef/browser/software_output_device_osr.h" + +#include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/render_widget_host_view_osr.h" +#include "libcef/browser/thread_util.h" + +#include "third_party/skia/include/core/SkDevice.h" +#include "ui/compositor/compositor.h" +#include "ui/gfx/skia_util.h" + +CefSoftwareOutputDeviceOSR::CefSoftwareOutputDeviceOSR( + ui::Compositor* compositor, + bool transparent, + const OnPaintCallback& callback) + : transparent_(transparent), + callback_(callback), + active_(false) { + CEF_REQUIRE_UIT(); + DCHECK(!callback_.is_null()); +} + +CefSoftwareOutputDeviceOSR::~CefSoftwareOutputDeviceOSR() { + CEF_REQUIRE_UIT(); +} + +void CefSoftwareOutputDeviceOSR::Resize(const gfx::Size& viewport_pixel_size, + float scale_factor) { + CEF_REQUIRE_UIT(); + + scale_factor_ = scale_factor; + + if (viewport_pixel_size_ == viewport_pixel_size) + return; + + viewport_pixel_size_ = viewport_pixel_size; + + canvas_.reset(NULL); + bitmap_.reset(new SkBitmap); + bitmap_->allocN32Pixels(viewport_pixel_size.width(), + viewport_pixel_size.height(), + !transparent_); + if (bitmap_->drawsNothing()) { + NOTREACHED(); + bitmap_.reset(NULL); + return; + } + + if (transparent_) + bitmap_->eraseARGB(0, 0, 0, 0); + + canvas_.reset(new SkCanvas(*bitmap_.get())); +} + +SkCanvas* CefSoftwareOutputDeviceOSR::BeginPaint(const gfx::Rect& damage_rect) { + CEF_REQUIRE_UIT(); + DCHECK(canvas_.get()); + DCHECK(bitmap_.get()); + + damage_rect_ = damage_rect; + + return canvas_.get(); +} + +void CefSoftwareOutputDeviceOSR::EndPaint(cc::SoftwareFrameData* frame_data) { + CEF_REQUIRE_UIT(); + DCHECK(canvas_.get()); + DCHECK(bitmap_.get()); + DCHECK(frame_data); + + if (!bitmap_.get()) + return; + + cc::SoftwareOutputDevice::EndPaint(frame_data); + + if (active_) + OnPaint(damage_rect_); +} + +void CefSoftwareOutputDeviceOSR::CopyToPixels(const gfx::Rect& rect, + void* pixels) { + CEF_REQUIRE_UIT(); + DCHECK(canvas_.get()); + SkImageInfo info = SkImageInfo::MakeN32Premul(rect.width(), rect.height()); + canvas_->readPixels(info, pixels, info.minRowBytes(), rect.x(), rect.y()); +} + +void CefSoftwareOutputDeviceOSR::SetActive(bool active) { + if (active == active_) + return; + active_ = active; + + // Call OnPaint immediately if deactivated while a damage rect is pending. + if (!active_ && !pending_damage_rect_.IsEmpty()) + OnPaint(pending_damage_rect_); +} + +void CefSoftwareOutputDeviceOSR::Invalidate(const gfx::Rect& damage_rect) { + if (pending_damage_rect_.IsEmpty()) + pending_damage_rect_ = damage_rect; + else + pending_damage_rect_.Union(damage_rect); +} + +void CefSoftwareOutputDeviceOSR::OnPaint(const gfx::Rect& damage_rect) { + gfx::Rect rect = damage_rect; + if (!pending_damage_rect_.IsEmpty()) { + rect.Union(pending_damage_rect_); + pending_damage_rect_.SetRect(0, 0, 0, 0); + } + + rect.Intersect(gfx::Rect(viewport_pixel_size_)); + if (rect.IsEmpty()) + return; + + SkAutoLockPixels bitmap_pixels_lock(*bitmap_.get()); + callback_.Run(rect, bitmap_->width(), bitmap_->height(), + bitmap_->getPixels()); +} diff --git a/libcef/browser/software_output_device_osr.h b/libcef/browser/software_output_device_osr.h new file mode 100644 index 000000000..de6a1aff6 --- /dev/null +++ b/libcef/browser/software_output_device_osr.h @@ -0,0 +1,56 @@ +// Copyright (c) 2014 The Chromium Embedded Framework Authors. +// Portions copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_SOFTWARE_OUTPUT_DEVICE_OSR_H_ +#define CEF_LIBCEF_BROWSER_SOFTWARE_OUTPUT_DEVICE_OSR_H_ + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "cc/output/software_output_device.h" + +namespace ui { +class Compositor; +} + +// Device implementation for direct software rendering via DelegatedFrameHost. +// All Rect/Size values are in pixels. +class CefSoftwareOutputDeviceOSR : public cc::SoftwareOutputDevice { + public: + typedef base::Callback + OnPaintCallback; + + CefSoftwareOutputDeviceOSR(ui::Compositor* compositor, + bool transparent, + const OnPaintCallback& callback); + ~CefSoftwareOutputDeviceOSR() override; + + // cc::SoftwareOutputDevice implementation. + void Resize(const gfx::Size& viewport_pixel_size, + float scale_factor) override; + SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override; + void EndPaint(cc::SoftwareFrameData* frame_data) override; + void CopyToPixels(const gfx::Rect& rect, void* pixels) override; + + void SetActive(bool active); + + // Include |damage_rect| the next time OnPaint is called. + void Invalidate(const gfx::Rect& damage_rect); + + // Deliver the OnPaint notification immediately. + void OnPaint(const gfx::Rect& damage_rect); + + private: + const bool transparent_; + const OnPaintCallback callback_; + + bool active_; + scoped_ptr canvas_; + scoped_ptr bitmap_; + gfx::Rect pending_damage_rect_; + + DISALLOW_COPY_AND_ASSIGN(CefSoftwareOutputDeviceOSR); +}; + +#endif // CEF_LIBCEF_BROWSER_SOFTWARE_OUTPUT_DEVICE_OSR_H_ diff --git a/patch/patch.cfg b/patch/patch.cfg index 37b397e96..772b02ab6 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -67,6 +67,12 @@ patches = [ 'name': 'public_browser_1161_1257', 'path': '../content/public/browser/', }, + { + # Allow creation of a custom SoftwareOutputDevice. + # http://code.google.com/p/chromiumembedded/issues/detail?id=1368 + 'name': 'compositor_1368', + 'path': '../', + }, { # Allow specification of a custom WebContentsView. # http://code.google.com/p/chromiumembedded/issues/detail?id=1257 diff --git a/patch/patches/compositor_1368.patch b/patch/patches/compositor_1368.patch new file mode 100644 index 000000000..41d183351 --- /dev/null +++ b/patch/patches/compositor_1368.patch @@ -0,0 +1,67 @@ +diff --git content/browser/compositor/gpu_process_transport_factory.cc content/browser/compositor/gpu_process_transport_factory.cc +index 33b3aa0..801598c 100644 +--- content/browser/compositor/gpu_process_transport_factory.cc ++++ content/browser/compositor/gpu_process_transport_factory.cc +@@ -106,6 +106,13 @@ GpuProcessTransportFactory::CreateOffscreenCommandBufferContext() { + + scoped_ptr CreateSoftwareOutputDevice( + ui::Compositor* compositor) { ++ if (compositor->delegate()) { ++ scoped_ptr output_device = ++ compositor->delegate()->CreateSoftwareOutputDevice(compositor); ++ if (output_device.get()) ++ return output_device; ++ } ++ + #if defined(OS_WIN) + return scoped_ptr(new SoftwareOutputDeviceWin( + compositor)); +diff --git ui/compositor/compositor.h ui/compositor/compositor.h +index 5d9d793..da20f14 100644 +--- ui/compositor/compositor.h ++++ ui/compositor/compositor.h +@@ -13,6 +13,7 @@ + #include "base/observer_list.h" + #include "base/single_thread_task_runner.h" + #include "base/time/time.h" ++#include "cc/output/software_output_device.h" + #include "cc/surfaces/surface_sequence.h" + #include "cc/trees/layer_tree_host_client.h" + #include "cc/trees/layer_tree_host_single_thread_client.h" +@@ -136,6 +137,17 @@ class COMPOSITOR_EXPORT CompositorLock + DISALLOW_COPY_AND_ASSIGN(CompositorLock); + }; + ++class COMPOSITOR_EXPORT CompositorDelegate { ++ public: ++ virtual scoped_ptr CreateSoftwareOutputDevice( ++ ui::Compositor* compositor) { ++ return scoped_ptr(); ++ } ++ ++ protected: ++ virtual ~CompositorDelegate() {} ++}; ++ + // Compositor object to take care of GPU painting. + // A Browser compositor object is responsible for generating the final + // displayable form of pixels comprising a single widget's contents. It draws an +@@ -157,6 +169,9 @@ class COMPOSITOR_EXPORT Compositor + // Schedules a redraw of the layer tree associated with this compositor. + void ScheduleDraw(); + ++ CompositorDelegate* delegate() const { return delegate_; } ++ void SetDelegate(CompositorDelegate* delegate) { delegate_ = delegate; } ++ + // Sets the root of the layer tree drawn by this Compositor. The root layer + // must have no parent. The compositor's root layer is reset if the root layer + // is destroyed. NULL can be passed to reset the root layer, in which case the +@@ -304,6 +319,8 @@ class COMPOSITOR_EXPORT Compositor + + ui::ContextFactory* context_factory_; + ++ CompositorDelegate* delegate_ = nullptr; ++ + // The root of the Layer tree drawn by this compositor. + Layer* root_layer_; + diff --git a/tests/cefclient/cefclient_osr_widget_mac.mm b/tests/cefclient/cefclient_osr_widget_mac.mm index 18a0d6632..855de69e4 100644 --- a/tests/cefclient/cefclient_osr_widget_mac.mm +++ b/tests/cefclient/cefclient_osr_widget_mac.mm @@ -82,6 +82,7 @@ class ScopedGLContext { button: (CefBrowserHost::MouseButtonType)type isUp: (bool)isUp; +- (CefRect) convertRectToBackingInternal: (const CefRect&) rect; - (CefRect) convertRectFromBackingInternal: (const CefRect&) rect; @property (readwrite, atomic) bool was_last_mouse_down_on_view; @@ -235,7 +236,8 @@ void ClientOSRHandler::OnPopupSize(CefRefPtr browser, if (!view_) return; - view_->renderer_->OnPopupSize(browser, rect); + view_->renderer_->OnPopupSize(browser, + [view_ convertRectToBackingInternal:rect]); } void ClientOSRHandler::OnPaint(CefRefPtr browser, @@ -259,10 +261,6 @@ void ClientOSRHandler::OnPaint(CefRefPtr browser, if (type == PET_VIEW && !view_->renderer_->popup_rect().IsEmpty()) { painting_popup_ = true; - CefRect client_popup_rect(0, 0, - view_->renderer_->popup_rect().width, - view_->renderer_->popup_rect().height); - browser->GetHost()->Invalidate(PET_POPUP); painting_popup_ = false; } @@ -333,7 +331,8 @@ void ClientOSRHandler::SetLoading(bool isLoading) { [self addTrackingArea:tracking_area_]; } - if ([self respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) { + if ([self respondsToSelector: + @selector(setWantsBestResolutionOpenGLSurface:)]) { // enable HiDPI buffer [self setWantsBestResolutionOpenGLSurface:YES]; } @@ -1193,10 +1192,11 @@ void ClientOSRHandler::SetLoading(bool isLoading) { } } -- (CefRect) convertRectFromBackingInternal: (const CefRect&) rect { - if ([self respondsToSelector:@selector(convertRectFromBacking:)]) { - NSRect old_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); - NSRect scaled_rect = [self convertRectFromBacking:old_rect]; +// Convert the rect from view coordinates to scaled coordinates. +- (CefRect) convertRectToBackingInternal: (const CefRect&) rect { + if ([self respondsToSelector:@selector(convertRectToBacking:)]) { + NSRect view_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); + NSRect scaled_rect = [self convertRectToBacking:view_rect]; return CefRect((int)scaled_rect.origin.x, (int)scaled_rect.origin.y, (int)scaled_rect.size.width, @@ -1206,6 +1206,20 @@ void ClientOSRHandler::SetLoading(bool isLoading) { return rect; } +// Convert the rect from scaled coordinates to view coordinates. +- (CefRect) convertRectFromBackingInternal: (const CefRect&) rect { + if ([self respondsToSelector:@selector(convertRectFromBacking:)]) { + NSRect scaled_rect = NSMakeRect(rect.x, rect.y, rect.width, rect.height); + NSRect view_rect = [self convertRectFromBacking:scaled_rect]; + return CefRect((int)view_rect.origin.x, + (int)view_rect.origin.y, + (int)view_rect.size.width, + (int)view_rect.size.height); + } + + return rect; +} + @end diff --git a/tests/cefclient/osrenderer.h b/tests/cefclient/osrenderer.h index 0a866a499..3ccb6b3a0 100644 --- a/tests/cefclient/osrenderer.h +++ b/tests/cefclient/osrenderer.h @@ -27,6 +27,7 @@ class ClientOSRenderer { // Forwarded from CefRenderHandler callbacks. void OnPopupShow(CefRefPtr browser, bool show); + // |rect| must be in pixel coordinates. void OnPopupSize(CefRefPtr browser, const CefRect& rect); void OnPaint(CefRefPtr browser, diff --git a/tests/unittests/os_rendering_unittest.cc b/tests/unittests/os_rendering_unittest.cc index 62e32bcaf..8d36ecbdc 100644 --- a/tests/unittests/os_rendering_unittest.cc +++ b/tests/unittests/os_rendering_unittest.cc @@ -7,6 +7,8 @@ #include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/gfx/geometry/dip_util.h" +#include "ui/gfx/geometry/rect.h" #include "include/base/cef_bind.h" #include "include/base/cef_logging.h" @@ -102,24 +104,6 @@ const int kVerticalScrollbarWidth = 14; #error "Unsupported platform" #endif // defined(OS_WIN) -// adjusted expected rect regarding system vertical scrollbar width -CefRect ExpectedRect(int index) { -#if defined(OS_WIN) || defined(OS_LINUX) - // this is the scrollbar width for all the kExpectedRectLI - if (kDefaultVerticalScrollbarWidth == kVerticalScrollbarWidth) - return kExpectedRectLI[index]; - - CefRect adjustedRect(kExpectedRectLI[index]); - adjustedRect.width += kDefaultVerticalScrollbarWidth - - kVerticalScrollbarWidth; - return adjustedRect; -#elif defined(OS_MACOSX) - return kExpectedRectLI[index]; -#else -#error "Unsupported platform" -#endif -} - // word to be written into edit box const char kKeyTestWord[] = "done"; @@ -208,8 +192,10 @@ class OSRTestHandler : public RoutingTestHandler, public CefRenderHandler, public CefContextMenuHandler { public: - explicit OSRTestHandler(OSRTestType test) - : test_type_(test), + OSRTestHandler(OSRTestType test_type, + float scale_factor) + : test_type_(test_type), + scale_factor_(scale_factor), event_count_(0), event_total_(1), started_(false) { @@ -343,8 +329,9 @@ class OSRTestHandler : public RoutingTestHandler, int& screenX, int& screenY) override { if (test_type_ == OSR_TEST_SCREEN_POINT && started()) { - EXPECT_EQ(viewX, MiddleX(ExpectedRect(4))); - EXPECT_EQ(viewY, MiddleY(ExpectedRect(4))); + const CefRect& expected_rect = GetExpectedRect(4); + EXPECT_EQ(viewX, MiddleX(expected_rect)); + EXPECT_EQ(viewY, MiddleY(expected_rect)); DestroySucceededTestSoon(); } else if (test_type_ == OSR_TEST_CONTEXT_MENU && started()){ screenX = 0; @@ -357,15 +344,15 @@ class OSRTestHandler : public RoutingTestHandler, bool GetScreenInfo(CefRefPtr browser, CefScreenInfo& screen_info) override { - screen_info.device_scale_factor = 1; + screen_info.device_scale_factor = scale_factor_; // The screen info rectangles are used by the renderer to create and - // position popups. If not overwritten in this function, the rectangle from - // returned GetViewRect will be used to popuplate them. + // position popups. If not overwritten in this function, the rectangle + // returned from GetViewRect will be used to popuplate them. // The popup in the test fits without modifications in the test window, so // setting the screen to the test window size does not affect its rectangle. screen_info.rect = CefRect(0, 0, kOsrWidth, kOsrHeight); - screen_info.available_rect = CefRect(0, 0, kOsrWidth, kOsrHeight); + screen_info.available_rect = screen_info.rect; return true; } @@ -418,11 +405,12 @@ class OSRTestHandler : public RoutingTestHandler, int width, int height) override { // bitmap must as big as GetViewRect said if (test_type_ != OSR_TEST_RESIZE && type == PET_VIEW) { - EXPECT_EQ(kOsrWidth, width); - EXPECT_EQ(kOsrHeight, height); + EXPECT_EQ(GetScaledInt(kOsrWidth), width); + EXPECT_EQ(GetScaledInt(kOsrHeight), height); } else if (type == PET_POPUP) { - EXPECT_EQ(kExpandedSelectRect.width, width); - EXPECT_EQ(kExpandedSelectRect.height, height); + const CefRect& expanded_select_rect = GetScaledRect(kExpandedSelectRect); + EXPECT_EQ(expanded_select_rect.width, width); + EXPECT_EQ(expanded_select_rect.height, height); } EXPECT_TRUE(browser->GetHost()->IsWindowRenderingDisabled()); @@ -441,7 +429,8 @@ class OSRTestHandler : public RoutingTestHandler, if (StartTest()) { // test that we have a full repaint EXPECT_EQ(dirtyRects.size(), 1U); - EXPECT_TRUE(IsFullRepaint(dirtyRects[0])); + EXPECT_TRUE(IsFullRepaint(dirtyRects[0], GetScaledInt(kOsrWidth), + GetScaledInt(kOsrHeight))); EXPECT_EQ(*(reinterpret_cast(buffer)), 0xffff8080); DestroySucceededTestSoon(); } @@ -450,7 +439,8 @@ class OSRTestHandler : public RoutingTestHandler, if (StartTest()) { // test that we have a full repaint EXPECT_EQ(dirtyRects.size(), 1U); - EXPECT_TRUE(IsFullRepaint(dirtyRects[0])); + EXPECT_TRUE(IsFullRepaint(dirtyRects[0], GetScaledInt(kOsrWidth), + GetScaledInt(kOsrHeight))); EXPECT_EQ(*(reinterpret_cast(buffer)), 0x7f7f0000U); DestroySucceededTestSoon(); } @@ -470,16 +460,18 @@ class OSRTestHandler : public RoutingTestHandler, mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, true); // enter mouse in the LI2 element having hand cursor - mouse_event.x = MiddleX(ExpectedRect(2)); - mouse_event.y = MiddleY(ExpectedRect(2)); + const CefRect& expected_rect = GetExpectedRect(2); + mouse_event.x = MiddleX(expected_rect); + mouse_event.y = MiddleY(expected_rect); browser->GetHost()->SendMouseMoveEvent(mouse_event, false); } break; case OSR_TEST_MOUSE_MOVE: if (StartTest()) { CefMouseEvent mouse_event; - mouse_event.x = MiddleX(ExpectedRect(3)); - mouse_event.y = MiddleY(ExpectedRect(3)); + const CefRect& expected_rect = GetExpectedRect(3); + mouse_event.x = MiddleX(expected_rect); + mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, false); } @@ -489,8 +481,9 @@ class OSRTestHandler : public RoutingTestHandler, case OSR_TEST_CONTEXT_MENU: if (StartTest()) { CefMouseEvent mouse_event; - mouse_event.x = MiddleX(ExpectedRect(4)); - mouse_event.y = MiddleY(ExpectedRect(4)); + const CefRect& expected_rect = GetExpectedRect(4); + mouse_event.x = MiddleX(expected_rect); + mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_RIGHT, false, 1); @@ -501,8 +494,9 @@ class OSRTestHandler : public RoutingTestHandler, case OSR_TEST_CLICK_LEFT: if (StartTest()) { CefMouseEvent mouse_event; - mouse_event.x = MiddleX(ExpectedRect(0)); - mouse_event.y = MiddleY(ExpectedRect(0)); + const CefRect& expected_rect = GetExpectedRect(0); + mouse_event.x = MiddleX(expected_rect); + mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( @@ -514,8 +508,9 @@ class OSRTestHandler : public RoutingTestHandler, case OSR_TEST_CLICK_MIDDLE: if (StartTest()) { CefMouseEvent mouse_event; - mouse_event.x = MiddleX(ExpectedRect(0)); - mouse_event.y = MiddleY(ExpectedRect(0)); + const CefRect& expected_rect = GetExpectedRect(0); + mouse_event.x = MiddleX(expected_rect); + mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseClickEvent( mouse_event, MBT_MIDDLE, false, 1); @@ -523,11 +518,13 @@ class OSRTestHandler : public RoutingTestHandler, mouse_event, MBT_MIDDLE, true, 1); } else { EXPECT_EQ(dirtyRects.size(), 1U); - CefRect expectedRect( - MiddleX(ExpectedRect(0)) - kMiddleButtonIconWidth / 2, - MiddleY(ExpectedRect(0)) - kMiddleButtonIconWidth / 2, + const CefRect& expected_rect = GetExpectedRect(0); + CefRect button_icon_rect( + MiddleX(expected_rect) - kMiddleButtonIconWidth / 2, + MiddleY(expected_rect) - kMiddleButtonIconWidth / 2, kMiddleButtonIconWidth, kMiddleButtonIconWidth); - EXPECT_EQ(dirtyRects[0], expectedRect); + button_icon_rect = GetScaledRect(button_icon_rect); + EXPECT_EQ(dirtyRects[0], button_icon_rect); DestroySucceededTestSoon(); } break; @@ -535,8 +532,8 @@ class OSRTestHandler : public RoutingTestHandler, if (StartTest()) { browser->GetHost()->WasResized(); } else { - EXPECT_EQ(kOsrWidth * 2, width); - EXPECT_EQ(kOsrHeight * 2, height); + EXPECT_EQ(GetScaledInt(kOsrWidth) * 2, width); + EXPECT_EQ(GetScaledInt(kOsrHeight) * 2, height); EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_TRUE(IsFullRepaint(dirtyRects[0], width, height)); DestroySucceededTestSoon(); @@ -547,7 +544,8 @@ class OSRTestHandler : public RoutingTestHandler, browser->GetHost()->Invalidate(PET_VIEW); } else { EXPECT_EQ(dirtyRects.size(), 1U); - EXPECT_EQ(dirtyRects[0], CefRect(0, 0, kOsrWidth, kOsrHeight)); + EXPECT_EQ(dirtyRects[0], + GetScaledRect(CefRect(0, 0, kOsrWidth, kOsrHeight))); DestroySucceededTestSoon(); } break; @@ -625,8 +623,9 @@ class OSRTestHandler : public RoutingTestHandler, case OSR_TEST_TOOLTIP: if (StartTest()) { CefMouseEvent mouse_event; - mouse_event.x = MiddleX(ExpectedRect(10)); - mouse_event.y = MiddleY(ExpectedRect(10)); + const CefRect& expected_rect = GetExpectedRect(10); + mouse_event.x = MiddleX(expected_rect); + mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseMoveEvent(mouse_event, false); } @@ -636,13 +635,15 @@ class OSRTestHandler : public RoutingTestHandler, if (StartTest()) { // scroll down once CefMouseEvent mouse_event; - mouse_event.x = MiddleX(ExpectedRect(0)); - mouse_event.y = MiddleY(ExpectedRect(0)); + const CefRect& expected_rect = GetExpectedRect(0); + mouse_event.x = MiddleX(expected_rect); + mouse_event.y = MiddleY(expected_rect); mouse_event.modifiers = 0; browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, - deltaY); } else { EXPECT_EQ(dirtyRects.size(), 1U); - EXPECT_EQ(dirtyRects[0], CefRect(0, 0, kOsrWidth, kOsrHeight)); + EXPECT_EQ(dirtyRects[0], + GetScaledRect(CefRect(0, 0, kOsrWidth, kOsrHeight))); DestroySucceededTestSoon(); } break; @@ -716,14 +717,16 @@ class OSRTestHandler : public RoutingTestHandler, ExpandDropDown(); } else if (type == PET_POPUP) { EXPECT_EQ(dirtyRects.size(), 1U); + const CefRect& expanded_select_rect = + GetScaledRect(kExpandedSelectRect); EXPECT_EQ(dirtyRects[0], CefRect(0, 0, - kExpandedSelectRect.width, - kExpandedSelectRect.height)); + expanded_select_rect.width, + expanded_select_rect.height)); // first pixel of border EXPECT_EQ(*(reinterpret_cast(buffer)), 0xff7f9db9); - EXPECT_EQ(kExpandedSelectRect.width, width); - EXPECT_EQ(kExpandedSelectRect.height, height); + EXPECT_EQ(expanded_select_rect.width, width); + EXPECT_EQ(expanded_select_rect.height, height); DestroySucceededTestSoon(); } break; @@ -743,12 +746,14 @@ class OSRTestHandler : public RoutingTestHandler, browser->GetHost()->SendMouseWheelEvent(mouse_event, 0, -10); scroll_inside_state = Scrolled; } else if (scroll_inside_state == Scrolled) { + const CefRect& expanded_select_rect = + GetScaledRect(kExpandedSelectRect); EXPECT_EQ(dirtyRects.size(), 1U); EXPECT_EQ(dirtyRects[0], CefRect(0, 0, - kExpandedSelectRect.width, - kExpandedSelectRect.height)); + expanded_select_rect.width, + expanded_select_rect.height)); DestroySucceededTestSoon(); } } @@ -868,8 +873,9 @@ class OSRTestHandler : public RoutingTestHandler, if (!started()) return; if (test_type_ == OSR_TEST_CLICK_RIGHT) { - EXPECT_EQ(params->GetXCoord(), MiddleX(ExpectedRect(4))); - EXPECT_EQ(params->GetYCoord(), MiddleY(ExpectedRect(4))); + const CefRect& expected_rect = GetExpectedRect(4); + EXPECT_EQ(params->GetXCoord(), MiddleX(expected_rect)); + EXPECT_EQ(params->GetYCoord(), MiddleY(expected_rect)); DestroySucceededTestSoon(); } else if (test_type_ == OSR_TEST_CONTEXT_MENU) { // This test will pass if it does not crash on destruction @@ -901,13 +907,38 @@ class OSRTestHandler : public RoutingTestHandler, CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, NULL); } - bool IsFullRepaint(const CefRect& rc, int width = kOsrWidth, - int height = kOsrHeight) { + CefRect GetScaledRect(const CefRect& rect) const { + const gfx::Rect& gfx_rect = gfx::ConvertRectToPixel( + scale_factor_, + gfx::Rect(rect.x, rect.y, rect.width, rect.height)); + return CefRect(gfx_rect.x(), gfx_rect.y(), + gfx_rect.width(), gfx_rect.height()); + } + + int GetScaledInt(int value) const { + const gfx::Point& gfx_point = gfx::ConvertPointToPixel( + scale_factor_, gfx::Point(value, 0)); + return gfx_point.x(); + } + + CefRect GetExpectedRect(int index) { + CefRect rect = kExpectedRectLI[index]; +#if defined(OS_WIN) || defined(OS_LINUX) + // Adjust the rect to include system vertical scrollbar width. + rect.width += kDefaultVerticalScrollbarWidth - kVerticalScrollbarWidth; +#elif !defined(OS_MACOSX) + #error "Unsupported platform" +#endif + + return rect; + } + + static bool IsFullRepaint(const CefRect& rc, int width, int height) { return rc.width == width && rc.height == height; } - static - bool IsBackgroundInBuffer(const uint32* buffer, size_t size, uint32 rgba) { + static bool IsBackgroundInBuffer(const uint32* buffer, size_t size, + uint32 rgba) { for (size_t i = 0; i < size; i++) { if (buffer[i] != rgba) { return false; @@ -959,6 +990,7 @@ class OSRTestHandler : public RoutingTestHandler, private: OSRTestType test_type_; + float scale_factor_; int event_count_; int event_total_; bool started_; @@ -969,42 +1001,69 @@ class OSRTestHandler : public RoutingTestHandler, } // namespace // generic test -#define OSR_TEST(name, test_mode)\ +#define OSR_TEST(name, test_mode, scale_factor)\ TEST(OSRTest, name) {\ CefRefPtr handler = \ - new OSRTestHandler(test_mode);\ + new OSRTestHandler(test_mode, scale_factor);\ handler->ExecuteTest();\ EXPECT_TRUE(handler->succeeded());\ } // tests -OSR_TEST(Windowless, OSR_TEST_IS_WINDOWLESS); -OSR_TEST(Focus, OSR_TEST_FOCUS); -OSR_TEST(Paint, OSR_TEST_PAINT); -OSR_TEST(TransparentPaint, OSR_TEST_TRANSPARENCY); -OSR_TEST(Cursor, OSR_TEST_CURSOR); -OSR_TEST(MouseMove, OSR_TEST_MOUSE_MOVE); -OSR_TEST(MouseRightClick, OSR_TEST_CLICK_RIGHT); -OSR_TEST(MouseLeftClick, OSR_TEST_CLICK_LEFT); +OSR_TEST(Windowless, OSR_TEST_IS_WINDOWLESS, 1.0f); +OSR_TEST(Windowless2x, OSR_TEST_IS_WINDOWLESS, 2.0f); +OSR_TEST(Focus, OSR_TEST_FOCUS, 1.0f); +OSR_TEST(Focus2x, OSR_TEST_FOCUS, 2.0f); +OSR_TEST(Paint, OSR_TEST_PAINT, 1.0f); +OSR_TEST(Paint2x, OSR_TEST_PAINT, 2.0f); +OSR_TEST(TransparentPaint, OSR_TEST_TRANSPARENCY, 1.0f); +OSR_TEST(TransparentPaint2x, OSR_TEST_TRANSPARENCY, 2.0f); +OSR_TEST(Cursor, OSR_TEST_CURSOR, 1.0f); +OSR_TEST(Cursor2x, OSR_TEST_CURSOR, 2.0f); +OSR_TEST(MouseMove, OSR_TEST_MOUSE_MOVE, 1.0f); +OSR_TEST(MouseMove2x, OSR_TEST_MOUSE_MOVE, 2.0f); +OSR_TEST(MouseRightClick, OSR_TEST_CLICK_RIGHT, 1.0f); +OSR_TEST(MouseRightClick2x, OSR_TEST_CLICK_RIGHT, 2.0f); +OSR_TEST(MouseLeftClick, OSR_TEST_CLICK_LEFT, 1.0f); +OSR_TEST(MouseLeftClick2x, OSR_TEST_CLICK_LEFT, 2.0f); #if !defined(OS_WIN) // The middle mouse click scroll icon is not currently shown on Windows. -OSR_TEST(MouseMiddleClick, OSR_TEST_CLICK_MIDDLE); +OSR_TEST(MouseMiddleClick, OSR_TEST_CLICK_MIDDLE, 1.0f); +OSR_TEST(MouseMiddleClick2x, OSR_TEST_CLICK_MIDDLE, 2.0f); #endif -OSR_TEST(ScreenPoint, OSR_TEST_SCREEN_POINT); -OSR_TEST(Resize, OSR_TEST_RESIZE); -OSR_TEST(Invalidate, OSR_TEST_INVALIDATE); -OSR_TEST(KeyEvents, OSR_TEST_KEY_EVENTS); -OSR_TEST(Tooltip, OSR_TEST_TOOLTIP); -OSR_TEST(Scrolling, OSR_TEST_SCROLLING); -OSR_TEST(ContextMenu, OSR_TEST_CONTEXT_MENU); -OSR_TEST(PopupPaint, OSR_TEST_POPUP_PAINT); -OSR_TEST(PopupShow, OSR_TEST_POPUP_SHOW); -OSR_TEST(PopupSize, OSR_TEST_POPUP_SIZE); -OSR_TEST(PopupHideOnBlur, OSR_TEST_POPUP_HIDE_ON_BLUR); -OSR_TEST(PopupHideOnClick, OSR_TEST_POPUP_HIDE_ON_CLICK); -OSR_TEST(PopupHideOnScroll, OSR_TEST_POPUP_HIDE_ON_SCROLL); -OSR_TEST(PopupHideOnEsc, OSR_TEST_POPUP_HIDE_ON_ESC); -OSR_TEST(PopupScrollInside, OSR_TEST_POPUP_SCROLL_INSIDE); -OSR_TEST(DragDropStartDragging, OSR_TEST_DRAG_DROP_START_DRAGGING); -OSR_TEST(DragDropUpdateCursor, OSR_TEST_DRAG_DROP_UPDATE_CURSOR); -OSR_TEST(DragDropDropElement, OSR_TEST_DRAG_DROP_DROP); +OSR_TEST(ScreenPoint, OSR_TEST_SCREEN_POINT, 1.0f); +OSR_TEST(ScreenPoint2x, OSR_TEST_SCREEN_POINT, 2.0f); +OSR_TEST(Resize, OSR_TEST_RESIZE, 1.0f); +OSR_TEST(Resize2x, OSR_TEST_RESIZE, 2.0f); +OSR_TEST(Invalidate, OSR_TEST_INVALIDATE, 1.0f); +OSR_TEST(Invalidate2x, OSR_TEST_INVALIDATE, 2.0f); +OSR_TEST(KeyEvents, OSR_TEST_KEY_EVENTS, 1.0f); +OSR_TEST(KeyEvents2x, OSR_TEST_KEY_EVENTS, 2.0f); +OSR_TEST(Tooltip, OSR_TEST_TOOLTIP, 1.0f); +OSR_TEST(Tooltip2x, OSR_TEST_TOOLTIP, 2.0f); +OSR_TEST(Scrolling, OSR_TEST_SCROLLING, 1.0f); +OSR_TEST(Scrolling2x, OSR_TEST_SCROLLING, 2.0f); +OSR_TEST(ContextMenu, OSR_TEST_CONTEXT_MENU, 1.0f); +OSR_TEST(ContextMenu2x, OSR_TEST_CONTEXT_MENU, 2.0f); +OSR_TEST(PopupPaint, OSR_TEST_POPUP_PAINT, 1.0f); +OSR_TEST(PopupPaint2x, OSR_TEST_POPUP_PAINT, 2.0f); +OSR_TEST(PopupShow, OSR_TEST_POPUP_SHOW, 1.0f); +OSR_TEST(PopupShow2x, OSR_TEST_POPUP_SHOW, 2.0f); +OSR_TEST(PopupSize, OSR_TEST_POPUP_SIZE, 1.0f); +OSR_TEST(PopupSize2x, OSR_TEST_POPUP_SIZE, 2.0f); +OSR_TEST(PopupHideOnBlur, OSR_TEST_POPUP_HIDE_ON_BLUR, 1.0f); +OSR_TEST(PopupHideOnBlur2x, OSR_TEST_POPUP_HIDE_ON_BLUR, 2.0f); +OSR_TEST(PopupHideOnClick, OSR_TEST_POPUP_HIDE_ON_CLICK, 1.0f); +OSR_TEST(PopupHideOnClick2x, OSR_TEST_POPUP_HIDE_ON_CLICK, 2.0f); +OSR_TEST(PopupHideOnScroll, OSR_TEST_POPUP_HIDE_ON_SCROLL, 1.0f); +OSR_TEST(PopupHideOnScroll2x, OSR_TEST_POPUP_HIDE_ON_SCROLL, 2.0f); +OSR_TEST(PopupHideOnEsc, OSR_TEST_POPUP_HIDE_ON_ESC, 1.0f); +OSR_TEST(PopupHideOnEsc2x, OSR_TEST_POPUP_HIDE_ON_ESC, 2.0f); +OSR_TEST(PopupScrollInside, OSR_TEST_POPUP_SCROLL_INSIDE, 1.0f); +OSR_TEST(PopupScrollInside2x, OSR_TEST_POPUP_SCROLL_INSIDE, 2.0f); +OSR_TEST(DragDropStartDragging, OSR_TEST_DRAG_DROP_START_DRAGGING, 1.0f); +OSR_TEST(DragDropStartDragging2x, OSR_TEST_DRAG_DROP_START_DRAGGING, 2.0f); +OSR_TEST(DragDropUpdateCursor, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 1.0f); +OSR_TEST(DragDropUpdateCursor2x, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 2.0f); +OSR_TEST(DragDropDropElement, OSR_TEST_DRAG_DROP_DROP, 1.0f); +OSR_TEST(DragDropDropElement2x, OSR_TEST_DRAG_DROP_DROP, 2.0f);