Commit 8ffbbc32 authored by miu@chromium.org's avatar miu@chromium.org

Account for DIP when setting the preferred size for tab capture.

This is an optimization for systems where there is more than one
physical pixel per logical pixel (e.g., Chromebook Pixel, Mac Retina).
On these systems, the render widget should be "preferably" sized such
that its physical size will match that of the tab capture frame size.

Because the entire graphics pipeline is handling N^2 less data, and also
will no longer need to down-scale images, users should see huge tab
capture performance improvements (e.g., higher frame rates, lower CPU
utilization and fan noise).

Expected side effect: Fullscreen-Within-Tab widgets will appear shrunk.
However, since content renderers are still DIP-aware, any UI controls
within the widget will appear right-sized.  In other words, user
interaction will not be diminished by this change.

Testing: Confirmed Fullscreen-Within-Tab and tab capture output is
correct by varying --force-device-scale-factor=X.

Review URL: https://codereview.chromium.org/461443003

Cr-Commit-Position: refs/heads/master@{#289040}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289040 0039d316-1c4b-4281-b951-d872f2087c98
parent 47ada20f
...@@ -80,6 +80,10 @@ ...@@ -80,6 +80,10 @@
#include "skia/ext/image_operations.h" #include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/display.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/screen.h"
namespace content { namespace content {
...@@ -273,11 +277,14 @@ class WebContentsCaptureMachine ...@@ -273,11 +277,14 @@ class WebContentsCaptureMachine
virtual void WebContentsDestroyed() OVERRIDE; virtual void WebContentsDestroyed() OVERRIDE;
private: private:
// Computes the preferred size of the target RenderWidget for optimal capture.
gfx::Size ComputeOptimalTargetSize() const;
// Starts observing the web contents, returning false if lookup fails. // Starts observing the web contents, returning false if lookup fails.
bool StartObservingWebContents(); bool StartObservingWebContents();
// Helper function to determine the view that we are currently tracking. // Helper function to determine the view that we are currently tracking.
RenderWidgetHost* GetTarget(); RenderWidgetHost* GetTarget() const;
// Response callback for RenderWidgetHost::CopyFromBackingStore(). // Response callback for RenderWidgetHost::CopyFromBackingStore().
void DidCopyFromBackingStore( void DidCopyFromBackingStore(
...@@ -675,7 +682,40 @@ void WebContentsCaptureMachine::Capture( ...@@ -675,7 +682,40 @@ void WebContentsCaptureMachine::Capture(
} }
} }
gfx::Size WebContentsCaptureMachine::ComputeOptimalTargetSize() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
gfx::Size optimal_size = oracle_proxy_->GetCaptureSize();
// If the ratio between physical and logical pixels is greater than 1:1,
// shrink |optimal_size| by that amount. Then, when external code resizes the
// render widget to the "preferred size," the widget will be physically
// rendered at the exact capture size, thereby eliminating unnecessary scaling
// operations in the graphics pipeline.
RenderWidgetHost* const rwh = GetTarget();
RenderWidgetHostView* const rwhv = rwh ? rwh->GetView() : NULL;
if (rwhv) {
const gfx::NativeView view = rwhv->GetNativeView();
gfx::Screen* const screen = gfx::Screen::GetScreenFor(view);
if (screen->IsDIPEnabled()) {
const gfx::Display display = screen->GetDisplayNearestWindow(view);
const float scale = display.device_scale_factor();
if (scale > 1.0f) {
const gfx::Size shrunk_size(
gfx::ToFlooredSize(gfx::ScaleSize(optimal_size, 1.0f / scale)));
if (shrunk_size.width() > 0 && shrunk_size.height() > 0)
optimal_size = shrunk_size;
}
}
}
VLOG(1) << "Computed optimal target size: " << optimal_size.ToString();
return optimal_size;
}
bool WebContentsCaptureMachine::StartObservingWebContents() { bool WebContentsCaptureMachine::StartObservingWebContents() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Look-up the RenderFrameHost and, from that, the WebContents that wraps it. // Look-up the RenderFrameHost and, from that, the WebContents that wraps it.
// If successful, begin observing the WebContents instance. // If successful, begin observing the WebContents instance.
// //
...@@ -694,7 +734,7 @@ bool WebContentsCaptureMachine::StartObservingWebContents() { ...@@ -694,7 +734,7 @@ bool WebContentsCaptureMachine::StartObservingWebContents() {
WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents()); WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
if (contents) { if (contents) {
contents->IncrementCapturerCount(oracle_proxy_->GetCaptureSize()); contents->IncrementCapturerCount(ComputeOptimalTargetSize());
fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID(); fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID();
RenewFrameSubscription(); RenewFrameSubscription();
return true; return true;
...@@ -710,7 +750,7 @@ void WebContentsCaptureMachine::WebContentsDestroyed() { ...@@ -710,7 +750,7 @@ void WebContentsCaptureMachine::WebContentsDestroyed() {
oracle_proxy_->ReportError("WebContentsDestroyed()"); oracle_proxy_->ReportError("WebContentsDestroyed()");
} }
RenderWidgetHost* WebContentsCaptureMachine::GetTarget() { RenderWidgetHost* WebContentsCaptureMachine::GetTarget() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!web_contents()) if (!web_contents())
return NULL; return NULL;
......
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
#include "skia/ext/platform_canvas.h" #include "skia/ext/platform_canvas.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"
namespace content { namespace content {
namespace { namespace {
...@@ -39,6 +41,7 @@ namespace { ...@@ -39,6 +41,7 @@ namespace {
const int kTestWidth = 320; const int kTestWidth = 320;
const int kTestHeight = 240; const int kTestHeight = 240;
const int kTestFramesPerSecond = 20; const int kTestFramesPerSecond = 20;
const float kTestDeviceScaleFactor = 2.0f;
const SkColor kNothingYet = 0xdeadbeef; const SkColor kNothingYet = 0xdeadbeef;
const SkColor kNotInterested = ~kNothingYet; const SkColor kNotInterested = ~kNothingYet;
...@@ -466,6 +469,49 @@ class StubClientObserver { ...@@ -466,6 +469,49 @@ class StubClientObserver {
DISALLOW_COPY_AND_ASSIGN(StubClientObserver); DISALLOW_COPY_AND_ASSIGN(StubClientObserver);
}; };
// A dummy implementation of gfx::Screen, since WebContentsVideoCaptureDevice
// needs access to a gfx::Display's device scale factor.
class FakeScreen : public gfx::Screen {
public:
FakeScreen() : the_one_display_(0x1337, gfx::Rect(0, 0, 2560, 1440)) {
the_one_display_.set_device_scale_factor(kTestDeviceScaleFactor);
}
virtual ~FakeScreen() {}
// gfx::Screen implementation (only what's needed for testing).
virtual bool IsDIPEnabled() OVERRIDE { return true; }
virtual gfx::Point GetCursorScreenPoint() OVERRIDE { return gfx::Point(); }
virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE { return NULL; }
virtual gfx::NativeWindow GetWindowAtScreenPoint(
const gfx::Point& point) OVERRIDE { return NULL; }
virtual int GetNumDisplays() const OVERRIDE { return 1; }
virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE {
return std::vector<gfx::Display>(1, the_one_display_);
}
virtual gfx::Display GetDisplayNearestWindow(
gfx::NativeView view) const OVERRIDE {
return the_one_display_;
}
virtual gfx::Display GetDisplayNearestPoint(
const gfx::Point& point) const OVERRIDE {
return the_one_display_;
}
virtual gfx::Display GetDisplayMatching(
const gfx::Rect& match_rect) const OVERRIDE {
return the_one_display_;
}
virtual gfx::Display GetPrimaryDisplay() const OVERRIDE {
return the_one_display_;
}
virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE {}
virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE {}
private:
gfx::Display the_one_display_;
DISALLOW_COPY_AND_ASSIGN(FakeScreen);
};
// Test harness that sets up a minimal environment with necessary stubs. // Test harness that sets up a minimal environment with necessary stubs.
class WebContentsVideoCaptureDeviceTest : public testing::Test { class WebContentsVideoCaptureDeviceTest : public testing::Test {
public: public:
...@@ -477,6 +523,9 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { ...@@ -477,6 +523,9 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test {
protected: protected:
virtual void SetUp() { virtual void SetUp() {
gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, &fake_screen_);
ASSERT_EQ(&fake_screen_, gfx::Screen::GetNativeScreen());
// TODO(nick): Sadness and woe! Much "mock-the-world" boilerplate could be // TODO(nick): Sadness and woe! Much "mock-the-world" boilerplate could be
// eliminated here, if only we could use RenderViewHostTestHarness. The // eliminated here, if only we could use RenderViewHostTestHarness. The
// catch is that we need our TestRenderViewHost to support a // catch is that we need our TestRenderViewHost to support a
...@@ -530,10 +579,13 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { ...@@ -530,10 +579,13 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test {
SiteInstanceImpl::set_render_process_host_factory(NULL); SiteInstanceImpl::set_render_process_host_factory(NULL);
render_view_host_factory_.reset(); render_view_host_factory_.reset();
render_process_host_factory_.reset(); render_process_host_factory_.reset();
gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, NULL);
} }
// Accessors. // Accessors.
CaptureTestSourceController* source() { return &controller_; } CaptureTestSourceController* source() { return &controller_; }
WebContents* web_contents() const { return web_contents_.get(); }
media::VideoCaptureDevice* device() { return device_.get(); } media::VideoCaptureDevice* device() { return device_.get(); }
void SimulateDrawEvent() { void SimulateDrawEvent() {
...@@ -558,6 +610,8 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { ...@@ -558,6 +610,8 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test {
} }
private: private:
FakeScreen fake_screen_;
StubClientObserver client_observer_; StubClientObserver client_observer_;
// The controller controls which pixel patterns to produce. // The controller controls which pixel patterns to produce.
...@@ -597,6 +651,11 @@ TEST_F(WebContentsVideoCaptureDeviceTest, InvalidInitialWebContentsError) { ...@@ -597,6 +651,11 @@ TEST_F(WebContentsVideoCaptureDeviceTest, InvalidInitialWebContentsError) {
} }
TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) { TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) {
const gfx::Size capture_preferred_size(
static_cast<int>(kTestWidth / kTestDeviceScaleFactor),
static_cast<int>(kTestHeight / kTestDeviceScaleFactor));
ASSERT_NE(capture_preferred_size, web_contents()->GetPreferredSize());
// We'll simulate the tab being closed after the capture pipeline is up and // We'll simulate the tab being closed after the capture pipeline is up and
// running. // running.
media::VideoCaptureParams capture_params; media::VideoCaptureParams capture_params;
...@@ -612,6 +671,10 @@ TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) { ...@@ -612,6 +671,10 @@ TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
// Check that the preferred size of the WebContents matches the one provided
// by WebContentsVideoCaptureDevice.
EXPECT_EQ(capture_preferred_size, web_contents()->GetPreferredSize());
// Post a task to close the tab. We should see an error reported to the // Post a task to close the tab. We should see an error reported to the
// consumer. // consumer.
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment