Commit c98fcadd authored by Khushal's avatar Khushal Committed by Commit Bot

viz/media: Plumb preferred frame interval for media.

Plumb the frame rate of video updates to the display compositor. This
information is used to configure the display to tick at the video's
frame rate if possible.

R=liberato@chromium.org

Bug: 938106
Change-Id: Ic720900181f58eddf65069c3a6d4cc480a98efb2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1556458
Commit-Queue: Eric Karl <ericrk@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Reviewed-by: default avatarEric Karl <ericrk@chromium.org>
Reviewed-by: default avatarStephen Chenney <schenney@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Auto-Submit: Khushal <khushalsagar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#652902}
parent 6aef680e
......@@ -93,6 +93,10 @@ class CC_EXPORT VideoFrameProvider {
// frame missed its intended deadline.
virtual void PutCurrentFrame() = 0;
// Returns the interval at which the provider expects to have new frames for
// the client.
virtual base::TimeDelta GetPreferredRenderInterval() = 0;
protected:
virtual ~VideoFrameProvider() {}
};
......
......@@ -4,6 +4,8 @@
#include "cc/test/fake_video_frame_provider.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
namespace cc {
FakeVideoFrameProvider::FakeVideoFrameProvider()
......@@ -35,4 +37,8 @@ void FakeVideoFrameProvider::PutCurrentFrame() {
++put_current_frame_count_;
}
base::TimeDelta FakeVideoFrameProvider::GetPreferredRenderInterval() {
return viz::BeginFrameArgs::MinInterval();
}
} // namespace cc
......@@ -22,6 +22,7 @@ class FakeVideoFrameProvider : public VideoFrameProvider {
bool HasCurrentFrame() override;
scoped_refptr<media::VideoFrame> GetCurrentFrame() override;
void PutCurrentFrame() override;
base::TimeDelta GetPreferredRenderInterval() override;
Client* client() { return client_; }
......
......@@ -148,6 +148,8 @@ class VIZ_COMMON_EXPORT CompositorFrameMetadata {
// was allocated.
base::TimeTicks local_surface_id_allocation_time;
base::Optional<base::TimeDelta> preferred_frame_interval;
#if defined(OS_ANDROID)
float max_page_scale_factor = 0.f;
gfx::SizeF root_layer_size;
......
......@@ -417,6 +417,11 @@ SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrameInternal(
}
}
if (frame.metadata.preferred_frame_interval) {
frame_sink_manager_->SetPreferredFrameIntervalForFrameSinkId(
frame_sink_id_, *frame.metadata.preferred_frame_interval);
}
Surface* prev_surface =
surface_manager_->GetSurfaceForId(last_created_surface_id_);
Surface* current_surface = nullptr;
......
......@@ -583,6 +583,14 @@ const CompositorFrameSinkSupport* FrameSinkManagerImpl::GetFrameSinkForId(
return nullptr;
}
void FrameSinkManagerImpl::SetPreferredFrameIntervalForFrameSinkId(
const FrameSinkId& id,
base::TimeDelta interval) {
auto it = frame_sink_data_.find(id);
DCHECK(it != frame_sink_data_.end());
it->second.preferred_frame_interval = interval;
}
base::TimeDelta FrameSinkManagerImpl::GetPreferredFrameIntervalForFrameSinkId(
const FrameSinkId& id) const {
auto it = frame_sink_data_.find(id);
......
......@@ -197,6 +197,8 @@ class VIZ_SERVICE_EXPORT FrameSinkManagerImpl
const CompositorFrameSinkSupport* GetFrameSinkForId(
const FrameSinkId& frame_sink_id) const;
void SetPreferredFrameIntervalForFrameSinkId(const FrameSinkId& id,
base::TimeDelta interval);
base::TimeDelta GetPreferredFrameIntervalForFrameSinkId(
const FrameSinkId& id) const;
......
......@@ -25,6 +25,7 @@
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "media/base/media_switches.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/android/window_android.h"
#include "url/gurl.h"
......@@ -46,31 +47,38 @@ class CompositorImplBrowserTest
CompositorImplBrowserTest() {}
void SetUp() override {
std::vector<base::Feature> features;
switch (GetParam()) {
case CompositorImplMode::kNormal:
break;
case CompositorImplMode::kViz:
scoped_feature_list_.InitAndEnableFeature(
features::kVizDisplayCompositor);
features =
std::vector<base::Feature>({features::kVizDisplayCompositor});
break;
case CompositorImplMode::kVizSkDDL:
scoped_feature_list_.InitWithFeatures(
features = std::vector<base::Feature>(
{features::kVizDisplayCompositor, features::kUseSkiaRenderer,
features::kDefaultEnableOopRasterization},
{});
features::kDefaultEnableOopRasterization});
break;
}
AppendFeatures(&features);
scoped_feature_list_.InitWithFeatures(features, {});
ContentBrowserTest::SetUp();
}
virtual std::string GetTestUrl() { return "/title1.html"; }
virtual void AppendFeatures(std::vector<base::Feature>* features) {}
protected:
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
ASSERT_TRUE(https_server.Start());
GURL http_url(embedded_test_server()->GetURL("/title1.html"));
GURL http_url(embedded_test_server()->GetURL(GetTestUrl()));
ASSERT_TRUE(NavigateToURL(shell(), http_url));
}
......@@ -91,7 +99,6 @@ class CompositorImplBrowserTest
web_contents()->GetRenderWidgetHostView());
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(CompositorImplBrowserTest);
......@@ -267,5 +274,40 @@ IN_PROC_BROWSER_TEST_P(CompositorImplBrowserTest,
CompositorSwapRunLoop(compositor_impl()).RunUntilSwap();
}
class CompositorImplBrowserTestRefreshRate
: public CompositorImplBrowserTest,
public ui::WindowAndroid::TestHooks {
public:
std::string GetTestUrl() override { return "/media/tulip2.webm"; }
void AppendFeatures(std::vector<base::Feature>* features) override {
features->push_back(media::kUseSurfaceLayerForVideo);
}
// WindowAndroid::TestHooks impl.
std::vector<float> GetSupportedRates() override {
return {120.f, 90.f, 60.f};
}
void SetPreferredRate(float refresh_rate) override {
if (fabs(refresh_rate - expected_refresh_rate_) < 2.f)
run_loop_->Quit();
}
float expected_refresh_rate_ = 0.f;
std::unique_ptr<base::RunLoop> run_loop_;
};
IN_PROC_BROWSER_TEST_P(CompositorImplBrowserTestRefreshRate, VideoPreference) {
window()->SetTestHooks(this);
expected_refresh_rate_ = 60.f;
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
run_loop_.reset();
window()->SetTestHooks(nullptr);
}
INSTANTIATE_TEST_SUITE_P(P,
CompositorImplBrowserTestRefreshRate,
::testing::Values(CompositorImplMode::kViz));
} // namespace
} // namespace content
......@@ -402,6 +402,11 @@ void WebMediaPlayerMSCompositor::PutCurrentFrame() {
current_frame_rendered_ = true;
}
base::TimeDelta WebMediaPlayerMSCompositor::GetPreferredRenderInterval() {
DCHECK(video_frame_compositor_task_runner_->BelongsToCurrentThread());
return viz::BeginFrameArgs::MinInterval();
}
void WebMediaPlayerMSCompositor::StartRendering() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
{
......
......@@ -101,6 +101,7 @@ class CONTENT_EXPORT WebMediaPlayerMSCompositor
bool HasCurrentFrame() override;
scoped_refptr<media::VideoFrame> GetCurrentFrame() override;
void PutCurrentFrame() override;
base::TimeDelta GetPreferredRenderInterval() override;
void StartRendering();
void StopRendering();
......
......@@ -43,5 +43,8 @@ specific_include_rules = {
],
"gpu_memory_buffer_video_frame_pool_unittest.cc": [
"+components/viz/test/test_context_provider.h",
],
"null_video_sink_unittest.cc": [
"+components/viz/common/frame_sinks/begin_frame_args.h",
]
}
......@@ -550,6 +550,7 @@ source_set("unit_tests") {
]
deps = [
"//base/test:test_support",
"//components/viz/common",
"//gpu/command_buffer/common",
"//media:test_support",
"//skia",
......
......@@ -9,6 +9,7 @@
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/test/simple_test_tick_clock.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/null_video_sink.h"
#include "media/base/test_helpers.h"
......@@ -46,6 +47,9 @@ class NullVideoSinkTest : public testing::Test,
gfx::Rect(natural_size), natural_size,
timestamp);
}
base::TimeDelta GetPreferredRenderInterval() override {
return viz::BeginFrameArgs::MinInterval();
}
// VideoRendererSink::RenderCallback implementation.
MOCK_METHOD3(Render,
......
......@@ -38,6 +38,10 @@ class MEDIA_EXPORT VideoRendererSink {
// not actually rendered. Must be called before the next Render() call.
virtual void OnFrameDropped() = 0;
// Returns the interval at which the sink expects to have new frames for the
// client.
virtual base::TimeDelta GetPreferredRenderInterval() = 0;
virtual ~RenderCallback() {}
};
......
......@@ -79,6 +79,7 @@ component("blink") {
deps = [
"//base",
"//cc",
"//components/viz/common",
"//gpu",
"//media",
"//media:shared_memory_support",
......
......@@ -4,6 +4,7 @@ include_rules = [
"+cc/layers/video_frame_provider.h",
"+cc/layers/video_layer.h",
"+components/scheduler", # Only allowed in tests.
"+components/viz/common/frame_sinks/begin_frame_args.h",
"+components/viz/common/gpu/context_provider.h",
"+components/viz/common/surfaces/frame_sink_id.h",
"+gin",
......
......@@ -8,6 +8,7 @@
#include "base/callback_helpers.h"
#include "base/time/default_tick_clock.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/media_switches.h"
#include "media/base/video_frame.h"
......@@ -176,6 +177,15 @@ bool VideoFrameCompositor::HasCurrentFrame() {
return static_cast<bool>(GetCurrentFrame());
}
base::TimeDelta VideoFrameCompositor::GetPreferredRenderInterval() {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock lock(callback_lock_);
if (!callback_)
return viz::BeginFrameArgs::MinInterval();
return callback_->GetPreferredRenderInterval();
}
void VideoFrameCompositor::Start(RenderCallback* callback) {
// Called from the media thread, so acquire the callback under lock before
// returning in case a Stop() call comes in before the PostTask is processed.
......
......@@ -93,6 +93,7 @@ class MEDIA_BLINK_EXPORT VideoFrameCompositor : public VideoRendererSink,
bool HasCurrentFrame() override;
scoped_refptr<VideoFrame> GetCurrentFrame() override;
void PutCurrentFrame() override;
base::TimeDelta GetPreferredRenderInterval() override;
// Returns |current_frame_|, without offering a guarantee as to how recently
// it was updated. In certain applications, one might need to periodically
......
......@@ -8,6 +8,7 @@
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/video_frame.h"
......@@ -107,6 +108,10 @@ class VideoFrameCompositorTest : public VideoRendererSink::RenderCallback,
bool));
MOCK_METHOD0(OnFrameDropped, void());
base::TimeDelta GetPreferredRenderInterval() override {
return preferred_render_interval_;
}
void StartVideoRendererSink() {
EXPECT_CALL(*submitter_, StartRendering());
const bool had_current_frame = !!compositor_->GetCurrentFrame();
......@@ -131,6 +136,7 @@ class VideoFrameCompositorTest : public VideoRendererSink::RenderCallback,
compositor()->PutCurrentFrame();
}
base::TimeDelta preferred_render_interval_;
base::SimpleTestTickClock tick_clock_;
StrictMock<MockWebVideoFrameSubmitter>* submitter_;
std::unique_ptr<StrictMock<MockWebVideoFrameSubmitter>> client_;
......@@ -341,6 +347,16 @@ TEST_P(VideoFrameCompositorTest, UpdateCurrentFrameIfStale) {
StopVideoRendererSink(false);
}
TEST_P(VideoFrameCompositorTest, PreferredRenderInterval) {
preferred_render_interval_ = base::TimeDelta::FromSeconds(1);
compositor_->Start(this);
EXPECT_EQ(compositor_->GetPreferredRenderInterval(),
preferred_render_interval_);
compositor_->Stop();
EXPECT_EQ(compositor_->GetPreferredRenderInterval(),
viz::BeginFrameArgs::MinInterval());
}
INSTANTIATE_TEST_SUITE_P(SubmitterEnabled,
VideoFrameCompositorTest,
::testing::Bool());
......
......@@ -328,6 +328,11 @@ void VideoRendererImpl::OnFrameDropped() {
algorithm_->OnLastFrameDropped();
}
base::TimeDelta VideoRendererImpl::GetPreferredRenderInterval() {
base::AutoLock auto_lock(lock_);
return algorithm_->average_frame_duration();
}
void VideoRendererImpl::OnVideoDecoderStreamInitialized(bool success) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
......
......@@ -84,6 +84,7 @@ class MEDIA_EXPORT VideoRendererImpl
base::TimeTicks deadline_max,
bool background_rendering) override;
void OnFrameDropped() override;
base::TimeDelta GetPreferredRenderInterval() override;
private:
// Callback for |video_decoder_stream_| initialization.
......
......@@ -885,6 +885,9 @@ TEST_F(VideoRendererImplTest, RenderingStartedThenStopped) {
// the previous call, the total should be 4 * 115200.
EXPECT_EQ(115200, last_pipeline_statistics.video_memory_usage);
EXPECT_EQ(renderer_->GetPreferredRenderInterval(),
last_pipeline_statistics.video_frame_duration_average);
// Consider the case that rendering is faster than we setup the test event.
// In that case, when we run out of the frames, BUFFERING_HAVE_NOTHING will
// be called. And then during SatisfyPendingDecodeWithEndOfStream,
......
......@@ -57,7 +57,8 @@ bool StructTraits<viz::mojom::CompositorFrameMetadataDataView,
data.ReadBeginFrameAck(&out->begin_frame_ack) &&
data.ReadLocalSurfaceIdAllocationTime(
&out->local_surface_id_allocation_time) &&
!out->local_surface_id_allocation_time.is_null();
!out->local_surface_id_allocation_time.is_null() &&
data.ReadPreferredFrameInterval(&out->preferred_frame_interval);
}
} // namespace mojo
......@@ -114,6 +114,11 @@ struct StructTraits<viz::mojom::CompositorFrameMetadataDataView,
return metadata.local_surface_id_allocation_time;
}
static base::Optional<base::TimeDelta> preferred_frame_interval(
const viz::CompositorFrameMetadata& metadata) {
return metadata.preferred_frame_interval;
}
#if defined(OS_ANDROID)
static float max_page_scale_factor(
const viz::CompositorFrameMetadata& metadata) {
......
......@@ -48,6 +48,12 @@ struct CompositorFrameMetadata {
mojo_base.mojom.TimeTicks local_surface_id_allocation_time;
// Specifies the interval at which the client's content is updated. This can
// be used to configure the display to the optimal vsync interval available.
// If unspecified, or set to BeginFrameArgs::MinInterval, it is assumed that
// the client can animate at the maximum frame rate supported by the Display.
mojo_base.mojom.TimeDelta? preferred_frame_interval;
[EnableIf=is_android]
float bottom_controls_height;
......
......@@ -52,6 +52,7 @@ class MockCompositorFrameSink : public viz::mojom::blink::CompositorFrameSink {
void(mojo::ScopedSharedBufferHandle,
gpu::mojom::blink::MailboxPtr));
MOCK_METHOD1(DidDeleteSharedBitmap, void(gpu::mojom::blink::MailboxPtr));
MOCK_METHOD1(SetPreferredFrameInterval, void(base::TimeDelta));
private:
mojo::Binding<viz::mojom::blink::CompositorFrameSink> binding_;
......
......@@ -490,6 +490,10 @@ viz::CompositorFrame VideoFrameSubmitter::CreateCompositorFrame(
viz::CompositorFrame compositor_frame;
compositor_frame.metadata.begin_frame_ack = begin_frame_ack;
compositor_frame.metadata.frame_token = ++next_frame_token_;
compositor_frame.metadata.preferred_frame_interval =
video_frame_provider_
? video_frame_provider_->GetPreferredRenderInterval()
: viz::BeginFrameArgs::MinInterval();
base::TimeTicks value;
if (video_frame && video_frame->metadata()->GetTimeTicks(
......
......@@ -48,6 +48,12 @@ class MockVideoFrameProvider : public cc::VideoFrameProvider {
MOCK_METHOD0(GetCurrentFrame, scoped_refptr<media::VideoFrame>());
MOCK_METHOD0(PutCurrentFrame, void());
base::TimeDelta GetPreferredRenderInterval() override {
return preferred_interval;
}
base::TimeDelta preferred_interval;
private:
DISALLOW_COPY_AND_ASSIGN(MockVideoFrameProvider);
};
......@@ -908,4 +914,34 @@ TEST_F(VideoFrameSubmitterTest, PageVisibilityControlsSubmission) {
scoped_task_environment_.RunUntilIdle();
}
TEST_F(VideoFrameSubmitterTest, PreferredInterval) {
video_frame_provider_->preferred_interval = base::TimeDelta::FromSeconds(1);
EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
submitter_->StartRendering();
scoped_task_environment_.RunUntilIdle();
EXPECT_CALL(*video_frame_provider_, UpdateCurrentFrame(_, _))
.WillOnce(Return(true));
EXPECT_CALL(*video_frame_provider_, GetCurrentFrame())
.WillOnce(Return(media::VideoFrame::CreateFrame(
media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
gfx::Size(8, 8), base::TimeDelta())));
EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
EXPECT_CALL(*video_frame_provider_, PutCurrentFrame());
EXPECT_CALL(*resource_provider_, AppendQuads(_, _, _, _));
EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
viz::BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs(
BEGINFRAME_FROM_HERE, now_src_.get());
submitter_->OnBeginFrame(args, {});
scoped_task_environment_.RunUntilIdle();
EXPECT_EQ(sink_->last_submitted_compositor_frame()
.metadata.preferred_frame_interval,
video_frame_provider_->preferred_interval);
}
} // namespace blink
......@@ -281,6 +281,9 @@ float WindowAndroid::GetRefreshRate() {
}
std::vector<float> WindowAndroid::GetSupportedRefreshRates() {
if (test_hooks_)
return test_hooks_->GetSupportedRates();
JNIEnv* env = AttachCurrentThread();
base::android::ScopedJavaLocalRef<jfloatArray> j_supported_refresh_rates =
Java_WindowAndroid_getSupportedRefreshRates(env, GetJavaObject());
......@@ -296,6 +299,11 @@ void WindowAndroid::SetPreferredRefreshRate(float refresh_rate) {
if (force_60hz_refresh_rate_)
return;
if (test_hooks_) {
test_hooks_->SetPreferredRate(refresh_rate);
return;
}
JNIEnv* env = AttachCurrentThread();
Java_WindowAndroid_setPreferredRefreshRate(env, GetJavaObject(),
refresh_rate);
......@@ -461,6 +469,17 @@ display::Display WindowAndroid::GetDisplayWithWindowColorSpace() {
return display;
}
void WindowAndroid::SetTestHooks(TestHooks* hooks) {
test_hooks_ = hooks;
if (!test_hooks_)
return;
if (compositor_) {
compositor_->OnUpdateSupportedRefreshRates(
test_hooks_->GetSupportedRates());
}
}
// ----------------------------------------------------------------------------
// Native JNI methods
// ----------------------------------------------------------------------------
......
......@@ -126,6 +126,14 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid {
void SetForce60HzRefreshRate();
class TestHooks {
public:
virtual ~TestHooks() = default;
virtual std::vector<float> GetSupportedRates() = 0;
virtual void SetPreferredRate(float refresh_rate) = 0;
};
void SetTestHooks(TestHooks* hooks);
private:
class WindowBeginFrameSource;
class ScopedOnBeginFrame;
......@@ -154,6 +162,7 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid {
float mouse_wheel_scroll_factor_;
bool vsync_paused_ = false;
TestHooks* test_hooks_ = nullptr;
bool force_60hz_refresh_rate_ = false;
DISALLOW_COPY_AND_ASSIGN(WindowAndroid);
......
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