Commit ed480cdc authored by liberato@chromium.org's avatar liberato@chromium.org Committed by Commit Bot

Refactor D3D11VideoDecoder for threading.

This CL moves all work that runs on the video decoder's thread into
D3D11VideoDecoder, and all GPU main thread work into Impl.

It does not actually move decoding to a different thread, nor does
it remove all of the threading assumptions from the code.  For
example, D3D11PictureBuffers still assume that they're created and
destroyed on the GPU main thread.

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel
Change-Id: I4ee52d84c77c57127d26803153c043959f7429ef
Reviewed-on: https://chromium-review.googlesource.com/1197165
Commit-Queue: Frank Liberato <liberato@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589224}
parent 889b4779
...@@ -38,7 +38,9 @@ D3D11PictureBuffer::D3D11PictureBuffer(GLenum target, ...@@ -38,7 +38,9 @@ D3D11PictureBuffer::D3D11PictureBuffer(GLenum target,
size_t level) size_t level)
: target_(target), size_(size), level_(level) {} : target_(target), size_(size), level_(level) {}
D3D11PictureBuffer::~D3D11PictureBuffer() {} D3D11PictureBuffer::~D3D11PictureBuffer() {
// TODO(liberato): post destruction of |gpu_resources_| to the gpu thread.
}
bool D3D11PictureBuffer::Init( bool D3D11PictureBuffer::Init(
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb, base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb,
...@@ -71,6 +73,7 @@ bool D3D11PictureBuffer::Init( ...@@ -71,6 +73,7 @@ bool D3D11PictureBuffer::Init(
// device for decoding. Sharing seems not to work very well. Otherwise, we // device for decoding. Sharing seems not to work very well. Otherwise, we
// would create the texture with KEYED_MUTEX and NTHANDLE, then send along // would create the texture with KEYED_MUTEX and NTHANDLE, then send along
// a handle that we get from |texture| as an IDXGIResource1. // a handle that we get from |texture| as an IDXGIResource1.
// TODO(liberato): this should happen on the gpu thread.
gpu_resources_ = std::make_unique<GpuResources>(); gpu_resources_ = std::make_unique<GpuResources>();
if (!gpu_resources_->Init(std::move(get_stub_cb), level_, if (!gpu_resources_->Init(std::move(get_stub_cb), level_,
std::move(mailboxes), target_, size_, texture, std::move(mailboxes), target_, size_, texture,
......
This diff is collapsed.
...@@ -10,29 +10,35 @@ ...@@ -10,29 +10,35 @@
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "gpu/config/gpu_driver_bug_workarounds.h" #include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_preferences.h" #include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/service/command_buffer_stub.h" #include "media/base/callback_registry.h"
#include "media/base/video_decoder.h" #include "media/base/video_decoder.h"
#include "media/gpu/media_gpu_export.h" #include "media/gpu/media_gpu_export.h"
#include "media/gpu/windows/d3d11_create_device_cb.h" #include "media/gpu/windows/d3d11_create_device_cb.h"
#include "media/gpu/windows/d3d11_h264_accelerator.h"
namespace gpu {
class CommandBufferStub;
} // namespace gpu
namespace media { namespace media {
class D3D11PictureBuffer;
class D3D11VideoDecoderImpl; class D3D11VideoDecoderImpl;
class D3D11VideoDecoderTest; class D3D11VideoDecoderTest;
class MediaLog; class MediaLog;
// Thread-hopping implementation of D3D11VideoDecoder. It's meant to run on // Video decoder that uses D3D11 directly. It is intended that this class will
// a random thread, and hop to the gpu main thread. It does this so that it // run the decoder on whatever thread it lives on. However, at the moment, it
// can use the D3D context etc. What should really happen is that we should // only works if it's on the gpu main thread.
// get (or share with other D3D11VideoDecoder instances) our own context, and class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
// just share the D3D texture with the main thread's context. However, for public D3D11VideoDecoderClient {
// now, it's easier to hop threads.
class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder {
public: public:
// |get_stub_cb| must be called from |gpu_task_runner|.
static std::unique_ptr<VideoDecoder> Create( static std::unique_ptr<VideoDecoder> Create(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
std::unique_ptr<MediaLog> media_log, std::unique_ptr<MediaLog> media_log,
...@@ -51,11 +57,16 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder { ...@@ -51,11 +57,16 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder {
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb) override; const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb) override;
void Decode(scoped_refptr<DecoderBuffer> buffer, void Decode(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb) override; const DecodeCB& decode_cb) override;
void Reset(const base::Closure& closure) override; void Reset(const base::RepeatingClosure& closure) override;
bool NeedsBitstreamConversion() const override; bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override; bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override; int GetMaxDecodeRequests() const override;
// D3D11VideoDecoderClient implementation.
D3D11PictureBuffer* GetPicture() override;
void OutputResult(D3D11PictureBuffer* buffer,
const VideoColorSpace& buffer_colorspace) override;
// Return false |config| definitely isn't going to work, so that we can fail // Return false |config| definitely isn't going to work, so that we can fail
// init without bothering with a thread hop. // init without bothering with a thread hop.
bool IsPotentiallySupported(const VideoDecoderConfig& config); bool IsPotentiallySupported(const VideoDecoderConfig& config);
...@@ -72,11 +83,27 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder { ...@@ -72,11 +83,27 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder {
private: private:
friend class D3D11VideoDecoderTest; friend class D3D11VideoDecoderTest;
D3D11VideoDecoder(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner, D3D11VideoDecoder(
std::unique_ptr<MediaLog> media_log, scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
const gpu::GpuPreferences& gpu_preferences, std::unique_ptr<MediaLog> media_log,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds, const gpu::GpuPreferences& gpu_preferences,
std::unique_ptr<D3D11VideoDecoderImpl> impl); const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
std::unique_ptr<D3D11VideoDecoderImpl> impl,
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb);
// Receive |buffer|, that is now unused by the client.
void ReceivePictureBufferFromClient(scoped_refptr<D3D11PictureBuffer> buffer);
// Called when the gpu side of initialization is complete.
void OnGpuInitComplete(bool success);
// Run the decoder loop.
void DoDecode();
// Create new PictureBuffers. Currently, this completes synchronously, but
// really should have an async interface since it must do some work on the gpu
// main thread.
void CreatePictureBuffers();
enum class D3D11VideoNotSupportedReason { enum class D3D11VideoNotSupportedReason {
kVideoIsSupported = 0, kVideoIsSupported = 0,
...@@ -104,16 +131,39 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder { ...@@ -104,16 +131,39 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder {
std::unique_ptr<MediaLog> media_log_; std::unique_ptr<MediaLog> media_log_;
enum class State {
// Initializing resources required to create a codec.
kInitializing,
// Initialization has completed and we're running. This is the only state
// in which |codec_| might be non-null. If |codec_| is null, a codec
// creation is pending.
kRunning,
// The decoder cannot make progress because it doesn't have the key to
// decrypt the buffer. Waiting for a new key to be available.
// This should only be transitioned from kRunning, and should only
// transition to kRunning.
kWaitingForNewKey,
// A fatal error occurred. A terminal state.
kError,
};
// Record a UMA about why IsPotentiallySupported returned false, or that it // Record a UMA about why IsPotentiallySupported returned false, or that it
// returned true. Also will add a MediaLog entry, etc. // returned true. Also will add a MediaLog entry, etc.
void SetWasSupportedReason(D3D11VideoNotSupportedReason enum_value); void SetWasSupportedReason(D3D11VideoNotSupportedReason enum_value);
// Callback to notify that new usable key is available.
void NotifyNewKey();
// Enter the kError state. This will fail any pending |init_cb_| and / or
// pending decode as well.
void NotifyError(const char* reason);
// The implementation, which we trampoline to the impl thread. // The implementation, which we trampoline to the impl thread.
// This must be freed on the impl thread. // This must be freed on the impl thread.
std::unique_ptr<D3D11VideoDecoderImpl> impl_; std::unique_ptr<D3D11VideoDecoderImpl> impl_;
// Weak ptr to |impl_|, which we use for callbacks. // Weak ptr to |impl_|, which we use for callbacks.
base::WeakPtr<VideoDecoder> impl_weak_; base::WeakPtr<D3D11VideoDecoderImpl> impl_weak_;
// Task runner for |impl_|. This must be the GPU main thread. // Task runner for |impl_|. This must be the GPU main thread.
scoped_refptr<base::SequencedTaskRunner> impl_task_runner_; scoped_refptr<base::SequencedTaskRunner> impl_task_runner_;
...@@ -121,8 +171,44 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder { ...@@ -121,8 +171,44 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder {
gpu::GpuPreferences gpu_preferences_; gpu::GpuPreferences gpu_preferences_;
gpu::GpuDriverBugWorkarounds gpu_workarounds_; gpu::GpuDriverBugWorkarounds gpu_workarounds_;
// During init, these will be set.
InitCB init_cb_;
OutputCB output_cb_;
bool is_encrypted_ = false;
D3D11CreateDeviceCB create_device_func_; D3D11CreateDeviceCB create_device_func_;
Microsoft::WRL::ComPtr<ID3D11Device> device_;
Microsoft::WRL::ComPtr<ID3D11DeviceContext> device_context_;
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device_;
Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context_;
std::unique_ptr<AcceleratedVideoDecoder> accelerated_video_decoder_;
GUID decoder_guid_;
std::list<std::pair<scoped_refptr<DecoderBuffer>, DecodeCB>>
input_buffer_queue_;
scoped_refptr<DecoderBuffer> current_buffer_;
DecodeCB current_decode_cb_;
base::TimeDelta current_timestamp_;
// Callback registration to keep the new key callback registered.
std::unique_ptr<CallbackRegistration> new_key_callback_registration_;
// Must be called on the gpu main thread. So, don't call it from here, since
// we don't know what thread we're on.
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb_;
// It would be nice to unique_ptr these, but we give a ref to the VideoFrame
// so that the texture is retained until the mailbox is opened.
std::vector<scoped_refptr<D3D11PictureBuffer>> picture_buffers_;
State state_ = State::kInitializing;
// Entire class should be single-sequence.
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<D3D11VideoDecoder> weak_factory_; base::WeakPtrFactory<D3D11VideoDecoder> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoder); DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoder);
......
...@@ -13,122 +13,71 @@ ...@@ -13,122 +13,71 @@
#include <string> #include <string>
#include <tuple> #include <tuple>
#include "base/callback.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/service/sequence_id.h" #include "gpu/command_buffer/service/sequence_id.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "media/base/callback_registry.h"
#include "media/base/video_decoder.h"
#include "media/base/video_decoder_config.h"
#include "media/gpu/gles2_decoder_helper.h"
#include "media/gpu/media_gpu_export.h" #include "media/gpu/media_gpu_export.h"
#include "media/gpu/windows/d3d11_h264_accelerator.h"
#include "media/gpu/windows/output_with_release_mailbox_cb.h" namespace gpu {
class CommandBufferStub;
struct SyncToken;
} // namespace gpu
namespace media { namespace media {
class MediaLog; class MediaLog;
class D3D11PictureBuffer;
class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl : public VideoDecoder, // Does the gpu main thread work for D3D11VideoDecoder. Except as noted, this
public D3D11VideoDecoderClient { // class lives on the GPU main thread.
// TODO(liberato): Rename this class as a follow-on to this refactor.
class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl {
public: public:
// May be constructed on any thread.
explicit D3D11VideoDecoderImpl( explicit D3D11VideoDecoderImpl(
std::unique_ptr<MediaLog> media_log, std::unique_ptr<MediaLog> media_log,
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb); base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb);
~D3D11VideoDecoderImpl() override; virtual ~D3D11VideoDecoderImpl();
// VideoDecoder implementation:
std::string GetDisplayName() const override;
void Initialize(
const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb,
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb) override;
void Decode(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb) override;
void Reset(const base::Closure& closure) override;
bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
// D3D11VideoDecoderClient implementation.
D3D11PictureBuffer* GetPicture() override;
void OutputResult(D3D11PictureBuffer* buffer,
const VideoColorSpace& buffer_colorspace) override;
// Return a weak ptr, since D3D11VideoDecoder constructs callbacks for us. using InitCB = base::OnceCallback<void(bool success)>;
base::WeakPtr<D3D11VideoDecoderImpl> GetWeakPtr();
private: // Returns a picture buffer that's no longer in use by the client.
enum class State { using ReturnPictureBufferCB =
// Initializing resources required to create a codec. base::RepeatingCallback<void(scoped_refptr<D3D11PictureBuffer>)>;
kInitializing,
// Initialization has completed and we're running. This is the only state // We will call back |init_cb| with the init status. |try_decoding_cb| should
// in which |codec_| might be non-null. If |codec_| is null, a codec // try to re-start decoding. We'll call this when we do something that might
// creation is pending. // allow decoding to make progress, such as reclaim a picture buffer.
kRunning, virtual void Initialize(InitCB init_cb,
// The decoder cannot make progress because it doesn't have the key to ReturnPictureBufferCB return_picture_buffer_cb);
// decrypt the buffer. Waiting for a new key to be available.
// This should only be transitioned from kRunning, and should only
// transition to kRunning.
kWaitingForNewKey,
// A fatal error occurred. A terminal state.
kError,
};
void DoDecode();
void CreatePictureBuffers();
// Called when the VideoFrame that uses |buffer| is freed.
void OnMailboxReleased(scoped_refptr<D3D11PictureBuffer> buffer, void OnMailboxReleased(scoped_refptr<D3D11PictureBuffer> buffer,
const gpu::SyncToken& sync_token); const gpu::SyncToken& sync_token);
void OnSyncTokenReleased(scoped_refptr<D3D11PictureBuffer> buffer);
// Callback to notify that new usable key is available. // Return a weak ptr, since D3D11VideoDecoder constructs callbacks for us.
void NotifyNewKey(); // May be called from any thread.
base::WeakPtr<D3D11VideoDecoderImpl> GetWeakPtr();
// Enter the kError state. This will fail any pending |init_cb_| and / or private:
// pending decode as well. void OnSyncTokenReleased(scoped_refptr<D3D11PictureBuffer> buffer);
void NotifyError(const char* reason);
std::unique_ptr<MediaLog> media_log_; std::unique_ptr<MediaLog> media_log_;
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb_; base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb_;
gpu::CommandBufferStub* stub_ = nullptr; gpu::CommandBufferStub* stub_ = nullptr;
Microsoft::WRL::ComPtr<ID3D11Device> device_;
Microsoft::WRL::ComPtr<ID3D11DeviceContext> device_context_;
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device_;
Microsoft::WRL::ComPtr<ID3D11VideoContext1> video_context_;
std::unique_ptr<AcceleratedVideoDecoder> accelerated_video_decoder_;
GUID decoder_guid_;
std::list<std::pair<scoped_refptr<DecoderBuffer>, DecodeCB>>
input_buffer_queue_;
scoped_refptr<DecoderBuffer> current_buffer_;
DecodeCB current_decode_cb_;
base::TimeDelta current_timestamp_;
// During init, these will be set.
InitCB init_cb_;
OutputCB output_cb_;
bool is_encrypted_ = false;
// It would be nice to unique_ptr these, but we give a ref to the VideoFrame
// so that the texture is retained until the mailbox is opened.
std::vector<scoped_refptr<D3D11PictureBuffer>> picture_buffers_;
State state_ = State::kInitializing;
// Callback registration to keep the new key callback registered.
std::unique_ptr<CallbackRegistration> new_key_callback_registration_;
// Wait sequence for sync points. // Wait sequence for sync points.
gpu::SequenceId wait_sequence_id_; gpu::SequenceId wait_sequence_id_;
// Called when we get a picture buffer back from the client.
ReturnPictureBufferCB return_picture_buffer_cb_;
// Has thread affinity -- must be run on the gpu main thread.
THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<D3D11VideoDecoderImpl> weak_factory_; base::WeakPtrFactory<D3D11VideoDecoderImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoderImpl); DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoderImpl);
......
...@@ -36,19 +36,12 @@ class MockD3D11VideoDecoderImpl : public D3D11VideoDecoderImpl { ...@@ -36,19 +36,12 @@ class MockD3D11VideoDecoderImpl : public D3D11VideoDecoderImpl {
nullptr, nullptr,
base::RepeatingCallback<gpu::CommandBufferStub*()>()) {} base::RepeatingCallback<gpu::CommandBufferStub*()>()) {}
MOCK_METHOD6( void Initialize(InitCB init_cb,
Initialize, ReturnPictureBufferCB return_picture_buffer_cb) override {
void(const VideoDecoderConfig& config, MockInitialize();
bool low_delay, }
CdmContext* cdm_context,
const InitCB& init_cb, MOCK_METHOD0(MockInitialize, void());
const OutputCB& output_cb,
const WaitingForDecryptionKeyCB& waiting_for_decryption_key_cb));
MOCK_METHOD2(Decode,
void(scoped_refptr<DecoderBuffer> buffer,
const DecodeCB& decode_cb));
MOCK_METHOD1(Reset, void(const base::RepeatingClosure& closure));
}; };
class D3D11VideoDecoderTest : public ::testing::Test { class D3D11VideoDecoderTest : public ::testing::Test {
...@@ -79,7 +72,9 @@ class D3D11VideoDecoderTest : public ::testing::Test { ...@@ -79,7 +72,9 @@ class D3D11VideoDecoderTest : public ::testing::Test {
decoder_ = base::WrapUnique<VideoDecoder>( decoder_ = base::WrapUnique<VideoDecoder>(
d3d11_decoder_raw_ = new D3D11VideoDecoder( d3d11_decoder_raw_ = new D3D11VideoDecoder(
gpu_task_runner_, nullptr /* MediaLog */, gpu_preferences_, gpu_task_runner_, nullptr /* MediaLog */, gpu_preferences_,
gpu_workarounds_, std::move(impl))); gpu_workarounds_, std::move(impl),
base::BindRepeating(
[]() -> gpu::CommandBufferStub* { return nullptr; })));
d3d11_decoder_raw_->SetCreateDeviceCallbackForTesting( d3d11_decoder_raw_->SetCreateDeviceCallbackForTesting(
base::BindRepeating(&D3D11CreateDeviceMock::Create, base::BindRepeating(&D3D11CreateDeviceMock::Create,
base::Unretained(&create_device_mock_))); base::Unretained(&create_device_mock_)));
...@@ -102,7 +97,7 @@ class D3D11VideoDecoderTest : public ::testing::Test { ...@@ -102,7 +97,7 @@ class D3D11VideoDecoderTest : public ::testing::Test {
if (expectation == kExpectSuccess) { if (expectation == kExpectSuccess) {
EXPECT_CALL(*this, MockInitCB(_)).Times(0); EXPECT_CALL(*this, MockInitCB(_)).Times(0);
EXPECT_CALL(*impl_, Initialize(_, low_delay, cdm_context, _, _, _)); EXPECT_CALL(*impl_, MockInitialize());
} else { } else {
EXPECT_CALL(*this, MockInitCB(false)); EXPECT_CALL(*this, MockInitCB(false));
} }
...@@ -164,7 +159,10 @@ TEST_F(D3D11VideoDecoderTest, SupportsH264) { ...@@ -164,7 +159,10 @@ TEST_F(D3D11VideoDecoderTest, SupportsH264) {
CreateDecoder(); CreateDecoder();
// Make sure that we're testing H264. // Make sure that we're testing H264.
ASSERT_EQ(supported_config_.profile(), H264PROFILE_MAIN); ASSERT_EQ(supported_config_.profile(), H264PROFILE_MAIN);
InitializeDecoder(supported_config_, kExpectSuccess); // We do not actually try to initialize the decoder, since we don't mock
// out enough of D3D for that to work. Instead, we just check that
// IsPotentiallySupported is correct.
EXPECT_TRUE(d3d11_decoder_raw_->IsPotentiallySupported(supported_config_));
} }
TEST_F(D3D11VideoDecoderTest, DoesNotSupportVP8) { TEST_F(D3D11VideoDecoderTest, DoesNotSupportVP8) {
......
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