Commit 7710e0b3 authored by lionel.g.landwerlin's avatar lionel.g.landwerlin Committed by Commit bot

gpu: media: RenderingHelper: wait for the display & window to be ready

With the display configuration now asynchronous, we can't immediately read the display size. We need to wait until the hardware is probed.

Also the VSyncProvider cannot trigger the given callback until the PlatformWindow is properly setup with the display. We need to wait for it the actually be resized.

BUG=447798
TEST=run video_decode_accelerator_unittest on freon

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

Cr-Commit-Position: refs/heads/master@{#313473}
parent 1cd6baf6
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/mac/scoped_nsautorelease_pool.h" #include "base/mac/scoped_nsautorelease_pool.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringize_macros.h" #include "base/strings/stringize_macros.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/time/time.h" #include "base/time/time.h"
...@@ -70,6 +71,30 @@ namespace content { ...@@ -70,6 +71,30 @@ namespace content {
#if defined(USE_OZONE) #if defined(USE_OZONE)
class DisplayConfiguratorObserver : public ui::DisplayConfigurator::Observer {
public:
DisplayConfiguratorObserver(base::RunLoop* loop) : loop_(loop) {}
~DisplayConfiguratorObserver() override {}
private:
// ui::DisplayConfigurator::Observer overrides:
void OnDisplayModeChanged(
const ui::DisplayConfigurator::DisplayStateList& outputs) override {
if (!loop_)
return;
loop_->Quit();
loop_ = nullptr;
}
void OnDisplayModeChangeFailed(
ui::MultipleDisplayState failed_new_state) override {
LOG(FATAL) << "Could not configure display";
}
base::RunLoop* loop_;
DISALLOW_COPY_AND_ASSIGN(DisplayConfiguratorObserver);
};
class RenderingHelper::StubOzoneDelegate : public ui::PlatformWindowDelegate { class RenderingHelper::StubOzoneDelegate : public ui::PlatformWindowDelegate {
public: public:
StubOzoneDelegate() : accelerated_widget_(gfx::kNullAcceleratedWidget) { StubOzoneDelegate() : accelerated_widget_(gfx::kNullAcceleratedWidget) {
...@@ -143,7 +168,7 @@ RenderingHelper::RenderedVideo::~RenderedVideo() { ...@@ -143,7 +168,7 @@ RenderingHelper::RenderedVideo::~RenderedVideo() {
} }
// static // static
bool RenderingHelper::InitializeOneOff() { void RenderingHelper::InitializeOneOff(base::WaitableEvent* done) {
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
#if GL_VARIANT_GLX #if GL_VARIANT_GLX
cmd_line->AppendSwitchASCII(switches::kUseGL, cmd_line->AppendSwitchASCII(switches::kUseGL,
...@@ -151,10 +176,10 @@ bool RenderingHelper::InitializeOneOff() { ...@@ -151,10 +176,10 @@ bool RenderingHelper::InitializeOneOff() {
#else #else
cmd_line->AppendSwitchASCII(switches::kUseGL, gfx::kGLImplementationEGLName); cmd_line->AppendSwitchASCII(switches::kUseGL, gfx::kGLImplementationEGLName);
#endif #endif
#if defined(USE_OZONE)
ui::OzonePlatform::InitializeForUI(); if (!gfx::GLSurface::InitializeOneOff())
#endif LOG(FATAL) << "Could not initialize GL";
return gfx::GLSurface::InitializeOneOff(); done->Signal();
} }
RenderingHelper::RenderingHelper() { RenderingHelper::RenderingHelper() {
...@@ -167,26 +192,7 @@ RenderingHelper::~RenderingHelper() { ...@@ -167,26 +192,7 @@ RenderingHelper::~RenderingHelper() {
Clear(); Clear();
} }
void RenderingHelper::Initialize(const RenderingHelperParams& params, void RenderingHelper::Setup() {
base::WaitableEvent* done) {
// Use videos_.size() != 0 as a proxy for the class having already been
// Initialize()'d, and UnInitialize() before continuing.
if (videos_.size()) {
base::WaitableEvent done(false, false);
UnInitialize(&done);
done.Wait();
}
render_task_.Reset(
base::Bind(&RenderingHelper::RenderContent, base::Unretained(this)));
frame_duration_ = params.rendering_fps > 0
? base::TimeDelta::FromSeconds(1) / params.rendering_fps
: base::TimeDelta();
render_as_thumbnails_ = params.render_as_thumbnails;
message_loop_ = base::MessageLoop::current();
#if defined(OS_WIN) #if defined(OS_WIN)
window_ = CreateWindowEx(0, window_ = CreateWindowEx(0,
L"Static", L"Static",
...@@ -229,21 +235,85 @@ void RenderingHelper::Initialize(const RenderingHelperParams& params, ...@@ -229,21 +235,85 @@ void RenderingHelper::Initialize(const RenderingHelperParams& params,
XSelectInput(display, window_, ExposureMask); XSelectInput(display, window_, ExposureMask);
XMapWindow(display, window_); XMapWindow(display, window_);
#elif defined(USE_OZONE) #elif defined(USE_OZONE)
base::MessageLoop::ScopedNestableTaskAllower nest_loop(
base::MessageLoop::current());
base::RunLoop wait_window_resize;
platform_window_delegate_.reset(new RenderingHelper::StubOzoneDelegate()); platform_window_delegate_.reset(new RenderingHelper::StubOzoneDelegate());
window_ = platform_window_delegate_->accelerated_widget(); window_ = platform_window_delegate_->accelerated_widget();
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
// We hold onto the main loop here to wait for the DisplayController
// to give us the size of the display so we can create a window of
// the same size.
base::RunLoop wait_display_setup;
DisplayConfiguratorObserver display_setup_observer(&wait_display_setup);
display_configurator_.reset(new ui::DisplayConfigurator()); display_configurator_.reset(new ui::DisplayConfigurator());
display_configurator_->AddObserver(&display_setup_observer);
display_configurator_->Init(true); display_configurator_->Init(true);
display_configurator_->ForceInitialConfigure(0); display_configurator_->ForceInitialConfigure(0);
// Make sure all the display configuration is applied.
wait_display_setup.Run();
display_configurator_->RemoveObserver(&display_setup_observer);
platform_window_delegate_->platform_window()->SetBounds( platform_window_delegate_->platform_window()->SetBounds(
gfx::Rect(display_configurator_->framebuffer_size())); gfx::Rect(display_configurator_->framebuffer_size()));
#else #else
platform_window_delegate_->platform_window()->SetBounds(gfx::Rect(800, 600)); platform_window_delegate_->platform_window()->SetBounds(gfx::Rect(800, 600));
#endif #endif
// On Ozone/DRI, platform windows are associated with the physical
// outputs. Association is achieved by matching the bounds of the
// window with the origin & modeset of the display output. Until a
// window is associated with a display output, we cannot get vsync
// events, because there is no hardware to get events from. Here we
// wait for the window to resized and therefore associated with
// display output to be sure that we will get such events.
wait_window_resize.RunUntilIdle();
#else #else
#error unknown platform #error unknown platform
#endif #endif
CHECK(window_ != gfx::kNullAcceleratedWidget); CHECK(window_ != gfx::kNullAcceleratedWidget);
}
void RenderingHelper::TearDown() {
#if defined(OS_WIN)
if (window_)
DestroyWindow(window_);
#elif defined(USE_X11)
// Destroy resources acquired in Initialize, in reverse-acquisition order.
if (window_) {
CHECK(XUnmapWindow(gfx::GetXDisplay(), window_));
CHECK(XDestroyWindow(gfx::GetXDisplay(), window_));
}
#elif defined(USE_OZONE)
platform_window_delegate_.reset();
#if defined(OS_CHROMEOS)
display_configurator_->PrepareForExit();
display_configurator_.reset();
#endif
#endif
window_ = gfx::kNullAcceleratedWidget;
}
void RenderingHelper::Initialize(const RenderingHelperParams& params,
base::WaitableEvent* done) {
// Use videos_.size() != 0 as a proxy for the class having already been
// Initialize()'d, and UnInitialize() before continuing.
if (videos_.size()) {
base::WaitableEvent done(false, false);
UnInitialize(&done);
done.Wait();
}
render_task_.Reset(
base::Bind(&RenderingHelper::RenderContent, base::Unretained(this)));
frame_duration_ = params.rendering_fps > 0
? base::TimeDelta::FromSeconds(1) / params.rendering_fps
: base::TimeDelta();
render_as_thumbnails_ = params.render_as_thumbnails;
message_loop_ = base::MessageLoop::current();
gl_surface_ = gfx::GLSurface::CreateViewGLSurface(window_); gl_surface_ = gfx::GLSurface::CreateViewGLSurface(window_);
screen_size_ = gl_surface_->GetSize(); screen_size_ = gl_surface_->GetSize();
...@@ -429,11 +499,6 @@ void RenderingHelper::WarmUpRendering(int warm_up_iterations) { ...@@ -429,11 +499,6 @@ void RenderingHelper::WarmUpRendering(int warm_up_iterations) {
void RenderingHelper::UnInitialize(base::WaitableEvent* done) { void RenderingHelper::UnInitialize(base::WaitableEvent* done) {
CHECK_EQ(base::MessageLoop::current(), message_loop_); CHECK_EQ(base::MessageLoop::current(), message_loop_);
#if defined(USE_OZONE) && defined(OS_CHROMEOS)
display_configurator_->PrepareForExit();
display_configurator_.reset();
#endif
render_task_.Cancel(); render_task_.Cancel();
if (render_as_thumbnails_) { if (render_as_thumbnails_) {
...@@ -578,20 +643,6 @@ void RenderingHelper::Clear() { ...@@ -578,20 +643,6 @@ void RenderingHelper::Clear() {
frame_count_ = 0; frame_count_ = 0;
thumbnails_fbo_id_ = 0; thumbnails_fbo_id_ = 0;
thumbnails_texture_id_ = 0; thumbnails_texture_id_ = 0;
#if defined(OS_WIN)
if (window_)
DestroyWindow(window_);
#elif defined(USE_X11)
// Destroy resources acquired in Initialize, in reverse-acquisition order.
if (window_) {
CHECK(XUnmapWindow(gfx::GetXDisplay(), window_));
CHECK(XDestroyWindow(gfx::GetXDisplay(), window_));
}
#elif defined(USE_OZONE)
platform_window_delegate_.reset();
#endif
window_ = gfx::kNullAcceleratedWidget;
} }
void RenderingHelper::GetThumbnailsAsRGB(std::vector<unsigned char>* rgb, void RenderingHelper::GetThumbnailsAsRGB(std::vector<unsigned char>* rgb,
......
...@@ -84,13 +84,25 @@ class RenderingHelper { ...@@ -84,13 +84,25 @@ class RenderingHelper {
RenderingHelper(); RenderingHelper();
~RenderingHelper(); ~RenderingHelper();
static bool InitializeOneOff(); // Initialize GL. This method must be called on the rendering
// thread.
static void InitializeOneOff(base::WaitableEvent* done);
// Create the render context and windows by the specified dimensions. // Setup the platform window to display test results. This method
// must be called on the main thread.
void Setup();
// Tear down the platform window. This method must be called on the
// main thread.
void TearDown();
// Create the render context and windows by the specified
// dimensions. This method must be called on the rendering thread.
void Initialize(const RenderingHelperParams& params, void Initialize(const RenderingHelperParams& params,
base::WaitableEvent* done); base::WaitableEvent* done);
// Undo the effects of Initialize() and signal |*done|. // Undo the effects of Initialize() and signal |*done|. This method
// must be called on the rendering thread.
void UnInitialize(base::WaitableEvent* done); void UnInitialize(base::WaitableEvent* done);
// Return a newly-created GLES2 texture id of the specified size, and // Return a newly-created GLES2 texture id of the specified size, and
......
...@@ -72,6 +72,10 @@ ...@@ -72,6 +72,10 @@
#error The VideoAccelerator tests are not supported on this platform. #error The VideoAccelerator tests are not supported on this platform.
#endif // OS_WIN #endif // OS_WIN
#if defined(USE_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif // defined(USE_OZONE)
using media::VideoDecodeAccelerator; using media::VideoDecodeAccelerator;
namespace content { namespace content {
...@@ -115,6 +119,10 @@ int g_rendering_warm_up = 0; ...@@ -115,6 +119,10 @@ int g_rendering_warm_up = 0;
// special value "0" means no override. // special value "0" means no override.
int g_num_play_throughs = 0; int g_num_play_throughs = 0;
// Environment to store rendering thread.
class VideoDecodeAcceleratorTestEnvironment;
VideoDecodeAcceleratorTestEnvironment* g_env;
// Magic constants for differentiating the reasons for NotifyResetDone being // Magic constants for differentiating the reasons for NotifyResetDone being
// called. // called.
enum ResetPoint { enum ResetPoint {
...@@ -203,6 +211,34 @@ enum ClientState { ...@@ -203,6 +211,34 @@ enum ClientState {
CS_MAX, // Must be last entry. CS_MAX, // Must be last entry.
}; };
// Initialize the GPU thread for rendering. We only need to setup once
// for all test cases.
class VideoDecodeAcceleratorTestEnvironment : public ::testing::Environment {
public:
VideoDecodeAcceleratorTestEnvironment()
: rendering_thread_("GLRenderingVDAClientThread") {}
void SetUp() override {
rendering_thread_.Start();
base::WaitableEvent done(false, false);
rendering_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&RenderingHelper::InitializeOneOff, &done));
done.Wait();
}
void TearDown() override { rendering_thread_.Stop(); }
scoped_refptr<base::SingleThreadTaskRunner> GetRenderingTaskRunner() const {
return rendering_thread_.task_runner();
}
private:
base::Thread rendering_thread_;
DISALLOW_COPY_AND_ASSIGN(VideoDecodeAcceleratorTestEnvironment);
};
// A helper class used to manage the lifetime of a Texture. // A helper class used to manage the lifetime of a Texture.
class TextureRef : public base::RefCounted<TextureRef> { class TextureRef : public base::RefCounted<TextureRef> {
public: public:
...@@ -941,52 +977,33 @@ class VideoDecodeAcceleratorTest : public ::testing::Test { ...@@ -941,52 +977,33 @@ class VideoDecodeAcceleratorTest : public ::testing::Test {
std::vector<TestVideoFile*> test_video_files_; std::vector<TestVideoFile*> test_video_files_;
RenderingHelper rendering_helper_; RenderingHelper rendering_helper_;
scoped_refptr<base::MessageLoopProxy> rendering_loop_proxy_;
private: private:
base::Thread rendering_thread_;
// Required for Thread to work. Not used otherwise. // Required for Thread to work. Not used otherwise.
base::ShadowingAtExitManager at_exit_manager_; base::ShadowingAtExitManager at_exit_manager_;
DISALLOW_COPY_AND_ASSIGN(VideoDecodeAcceleratorTest); DISALLOW_COPY_AND_ASSIGN(VideoDecodeAcceleratorTest);
}; };
VideoDecodeAcceleratorTest::VideoDecodeAcceleratorTest() VideoDecodeAcceleratorTest::VideoDecodeAcceleratorTest() {
: rendering_thread_("GLRenderingVDAClientThread") {} }
void VideoDecodeAcceleratorTest::SetUp() { void VideoDecodeAcceleratorTest::SetUp() {
ParseAndReadTestVideoData(g_test_video_data, &test_video_files_); ParseAndReadTestVideoData(g_test_video_data, &test_video_files_);
// Initialize the rendering thread.
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_DEFAULT;
#if defined(OS_WIN) || defined(USE_OZONE)
// For windows the decoding thread initializes the media foundation decoder
// which uses COM. We need the thread to be a UI thread.
// On Ozone, the backend initializes the event system using a UI
// thread.
options.message_loop_type = base::MessageLoop::TYPE_UI;
#endif // OS_WIN || USE_OZONE
rendering_thread_.StartWithOptions(options);
rendering_loop_proxy_ = rendering_thread_.message_loop_proxy();
} }
void VideoDecodeAcceleratorTest::TearDown() { void VideoDecodeAcceleratorTest::TearDown() {
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE, base::Bind(&STLDeleteElements<std::vector<TestVideoFile*>>,
base::Bind(&STLDeleteElements<std::vector<TestVideoFile*> >, &test_video_files_));
&test_video_files_));
base::WaitableEvent done(false, false); base::WaitableEvent done(false, false);
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE, base::Bind(&RenderingHelper::UnInitialize,
base::Bind(&RenderingHelper::UnInitialize, base::Unretained(&rendering_helper_), &done));
base::Unretained(&rendering_helper_),
&done));
done.Wait(); done.Wait();
rendering_thread_.Stop(); rendering_helper_.TearDown();
} }
void VideoDecodeAcceleratorTest::ParseAndReadTestVideoData( void VideoDecodeAcceleratorTest::ParseAndReadTestVideoData(
...@@ -1054,23 +1071,22 @@ void VideoDecodeAcceleratorTest::UpdateTestVideoFileParams( ...@@ -1054,23 +1071,22 @@ void VideoDecodeAcceleratorTest::UpdateTestVideoFileParams(
void VideoDecodeAcceleratorTest::InitializeRenderingHelper( void VideoDecodeAcceleratorTest::InitializeRenderingHelper(
const RenderingHelperParams& helper_params) { const RenderingHelperParams& helper_params) {
rendering_helper_.Setup();
base::WaitableEvent done(false, false); base::WaitableEvent done(false, false);
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&RenderingHelper::Initialize, base::Bind(&RenderingHelper::Initialize,
base::Unretained(&rendering_helper_), base::Unretained(&rendering_helper_), helper_params, &done));
helper_params,
&done));
done.Wait(); done.Wait();
} }
void VideoDecodeAcceleratorTest::CreateAndStartDecoder( void VideoDecodeAcceleratorTest::CreateAndStartDecoder(
GLRenderingVDAClient* client, GLRenderingVDAClient* client,
ClientStateNotification<ClientState>* note) { ClientStateNotification<ClientState>* note) {
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE, base::Bind(&GLRenderingVDAClient::CreateAndStartDecoder,
base::Bind(&GLRenderingVDAClient::CreateAndStartDecoder, base::Unretained(client)));
base::Unretained(client)));
ASSERT_EQ(note->Wait(), CS_DECODER_SET); ASSERT_EQ(note->Wait(), CS_DECODER_SET);
} }
...@@ -1084,7 +1100,7 @@ void VideoDecodeAcceleratorTest::WaitUntilDecodeFinish( ...@@ -1084,7 +1100,7 @@ void VideoDecodeAcceleratorTest::WaitUntilDecodeFinish(
void VideoDecodeAcceleratorTest::WaitUntilIdle() { void VideoDecodeAcceleratorTest::WaitUntilIdle() {
base::WaitableEvent done(false, false); base::WaitableEvent done(false, false);
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
done.Wait(); done.Wait();
...@@ -1299,11 +1315,10 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) { ...@@ -1299,11 +1315,10 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) {
std::vector<unsigned char> rgb; std::vector<unsigned char> rgb;
bool alpha_solid; bool alpha_solid;
base::WaitableEvent done(false, false); base::WaitableEvent done(false, false);
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE, base::Bind(&RenderingHelper::GetThumbnailsAsRGB,
base::Bind(&RenderingHelper::GetThumbnailsAsRGB, base::Unretained(&rendering_helper_), &rgb,
base::Unretained(&rendering_helper_), &alpha_solid, &done));
&rgb, &alpha_solid, &done));
done.Wait(); done.Wait();
std::vector<std::string> golden_md5s; std::vector<std::string> golden_md5s;
...@@ -1349,14 +1364,14 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) { ...@@ -1349,14 +1364,14 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) {
} }
} }
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&STLDeleteElements<std::vector<GLRenderingVDAClient*> >, base::Bind(&STLDeleteElements<std::vector<GLRenderingVDAClient*>>,
&clients)); &clients));
rendering_loop_proxy_->PostTask( g_env->GetRenderingTaskRunner()->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&STLDeleteElements< base::Bind(&STLDeleteElements<
std::vector<ClientStateNotification<ClientState>*> >, std::vector<ClientStateNotification<ClientState>*>>,
&notes)); &notes));
WaitUntilIdle(); WaitUntilIdle();
}; };
...@@ -1479,8 +1494,8 @@ TEST_F(VideoDecodeAcceleratorTest, TestDecodeTimeMedian) { ...@@ -1479,8 +1494,8 @@ TEST_F(VideoDecodeAcceleratorTest, TestDecodeTimeMedian) {
if (g_output_log != NULL) if (g_output_log != NULL)
OutputLogFile(g_output_log, output_string); OutputLogFile(g_output_log, output_string);
rendering_loop_proxy_->DeleteSoon(FROM_HERE, client); g_env->GetRenderingTaskRunner()->DeleteSoon(FROM_HERE, client);
rendering_loop_proxy_->DeleteSoon(FROM_HERE, note); g_env->GetRenderingTaskRunner()->DeleteSoon(FROM_HERE, note);
WaitUntilIdle(); WaitUntilIdle();
}; };
...@@ -1548,8 +1563,24 @@ int main(int argc, char **argv) { ...@@ -1548,8 +1563,24 @@ int main(int argc, char **argv) {
} }
base::ShadowingAtExitManager at_exit_manager; base::ShadowingAtExitManager at_exit_manager;
#if defined(OS_WIN) || defined(USE_OZONE)
// For windows the decoding thread initializes the media foundation decoder
// which uses COM. We need the thread to be a UI thread.
// On Ozone, the backend initializes the event system using a UI
// thread.
base::MessageLoopForUI main_loop;
#else
base::MessageLoop main_loop; base::MessageLoop main_loop;
content::RenderingHelper::InitializeOneOff(); #endif // OS_WIN || USE_OZONE
#if defined(USE_OZONE)
ui::OzonePlatform::InitializeForUI();
#endif
content::g_env =
reinterpret_cast<content::VideoDecodeAcceleratorTestEnvironment*>(
testing::AddGlobalTestEnvironment(
new content::VideoDecodeAcceleratorTestEnvironment()));
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }
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