Commit 42fdcfd1 authored by Jianpeng Chao's avatar Jianpeng Chao Committed by Commit Bot

Revert "[media] VdaVideoDecoder"

This reverts commit e192695d.

Reason for revert: Buildbot failed.

Original change's description:
> [media] VdaVideoDecoder
> 
> This adds VdaVideoDecoder, an adapter that implements the media::VideoDecoder interface using a
> media::VideoDecodeAccelerator. VdaVideoDecoder expects to run in the GPU process, with access
> to a command buffer stub, as will be the case for decoders created by MojoVideoDecoder.
> 
> VdaVideoDecoder runs on the (mojo) IO thread as much as possible, but the VDA interface is
> inherently tied to the GPU 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:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
> Change-Id: I23f81c92b9ad72a5f0141c2eec2a528de1ffa9d3
> Bug: 522298
> Reviewed-on: https://chromium-review.googlesource.com/940336
> Reviewed-by: Xiaohan Wang <xhwang@chromium.org>
> Reviewed-by: Frank Liberato <liberato@chromium.org>
> Commit-Queue: Dan Sanders <sandersd@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#550471}

TBR=xhwang@chromium.org,sandersd@chromium.org,liberato@chromium.org

Change-Id: I706bf6a9b8b31d430b5facd0151e7e20dc73bcfb
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 522298, 832484
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:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Reviewed-on: https://chromium-review.googlesource.com/1011709Reviewed-by: default avatarJianpeng Chao <chaopeng@chromium.org>
Commit-Queue: Jianpeng Chao <chaopeng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550480}
parent c38bba2d
......@@ -124,7 +124,6 @@ source_set("test_support") {
"//media/base/android:test_support",
"//media/filters:test_support",
"//media/formats:test_support",
"//media/gpu:test_support",
"//media/video:test_support",
]
}
......
......@@ -87,8 +87,6 @@ component("gpu") {
defines = [ "MEDIA_GPU_IMPLEMENTATION" ]
sources = [
"command_buffer_helper.cc",
"command_buffer_helper.h",
"fake_jpeg_decode_accelerator.cc",
"fake_jpeg_decode_accelerator.h",
"fake_video_decode_accelerator.cc",
......@@ -113,7 +111,6 @@ component("gpu") {
"//base",
"//gpu",
"//media",
"//third_party/mesa:mesa_headers",
"//ui/gfx/geometry",
]
deps = [
......@@ -501,6 +498,7 @@ if (use_vaapi) {
"//base/test:test_support",
"//media:test_support",
"//media/gpu",
"//media/gpu/ipc/service",
"//testing/gtest",
"//third_party:jpeg",
"//third_party/libyuv",
......@@ -531,6 +529,7 @@ if (use_v4l2_codec || use_vaapi) {
"//base",
"//media:test_support",
"//media/gpu",
"//media/gpu/ipc/service",
"//media/mojo/services",
"//testing/gtest",
"//third_party/libyuv",
......@@ -555,30 +554,12 @@ if (use_v4l2_codec || use_vaapi) {
}
}
static_library("test_support") {
visibility = [ "//media:test_support" ]
testonly = true
sources = [
"fake_command_buffer_helper.cc",
"fake_command_buffer_helper.h",
]
configs += [ "//media:media_config" ]
deps = [
":gpu",
]
public_deps = [
"//base",
"//media",
]
}
source_set("unit_tests") {
testonly = true
deps = [
"//base",
"//media:test_support",
"//media/gpu",
"//media/gpu/ipc/service:unit_tests",
"//testing/gmock",
"//testing/gtest",
]
......
// Copyright 2018 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 "media/gpu/command_buffer_helper.h"
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/common/scheduling_priority.h"
#include "gpu/command_buffer/service/decoder_context.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "media/gpu/gles2_decoder_helper.h"
#include "ui/gl/gl_context.h"
namespace media {
namespace {
class CommandBufferHelperImpl
: public CommandBufferHelper,
public gpu::CommandBufferStub::DestructionObserver {
public:
explicit CommandBufferHelperImpl(gpu::CommandBufferStub* stub) : stub_(stub) {
DVLOG(1) << __func__;
DCHECK(stub_);
DCHECK(stub_->channel()->task_runner()->BelongsToCurrentThread());
stub_->AddDestructionObserver(this);
wait_sequence_id_ = stub_->channel()->scheduler()->CreateSequence(
gpu::SchedulingPriority::kNormal);
decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context());
}
bool MakeContextCurrent() override {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return decoder_helper_ && decoder_helper_->MakeContextCurrent();
}
GLuint CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) override {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(stub_->decoder_context()->GetGLContext()->IsCurrent(nullptr));
scoped_refptr<gpu::gles2::TextureRef> texture_ref =
decoder_helper_->CreateTexture(target, internal_format, width, height,
format, type);
GLuint service_id = texture_ref->service_id();
texture_refs_[service_id] = std::move(texture_ref);
return service_id;
}
void DestroyTexture(GLuint service_id) override {
DVLOG(2) << __func__ << "(" << service_id << ")";
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(stub_->decoder_context()->GetGLContext()->IsCurrent(nullptr));
DCHECK(texture_refs_.count(service_id));
texture_refs_.erase(service_id);
}
gpu::Mailbox CreateMailbox(GLuint service_id) override {
DVLOG(2) << __func__ << "(" << service_id << ")";
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!decoder_helper_)
return gpu::Mailbox();
DCHECK(texture_refs_.count(service_id));
return decoder_helper_->CreateMailbox(texture_refs_[service_id].get());
}
void SetCleared(GLuint service_id) override {
DVLOG(2) << __func__ << "(" << service_id << ")";
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!decoder_helper_)
return;
DCHECK(texture_refs_.count(service_id));
decoder_helper_->SetCleared(texture_refs_[service_id].get());
}
void WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) override {
DVLOG(2) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!stub_)
return;
// TODO(sandersd): Do we need to keep a ref to |this| while there are
// pending waits? If we destruct while they are pending, they will never
// run.
stub_->channel()->scheduler()->ScheduleTask(
gpu::Scheduler::Task(wait_sequence_id_, std::move(done_cb),
std::vector<gpu::SyncToken>({sync_token})));
}
private:
~CommandBufferHelperImpl() override {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (!stub_)
return;
// Try to drop TextureRefs with the context current, so that the platform
// textures can be deleted.
//
// Note: Since we don't know what stack we are on, it might not be safe to
// change the context. In practice we can be reasonably sure that our last
// owner isn't doing work in a different context.
//
// TODO(sandersd): We should restore the previous context.
if (!texture_refs_.empty() && MakeContextCurrent())
texture_refs_.clear();
DestroyStub();
}
void OnWillDestroyStub() override {
DVLOG(1) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// OnWillDestroyStub() is called with the context current if possible. Drop
// the TextureRefs now while the platform textures can still be deleted.
texture_refs_.clear();
DestroyStub();
}
void DestroyStub() {
DVLOG(3) << __func__;
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
decoder_helper_ = nullptr;
// If the last reference to |this| is in a |done_cb|, destroying the wait
// sequence can delete |this|. Clearing |stub_| first prevents DestroyStub()
// being called twice.
gpu::CommandBufferStub* stub = stub_;
stub_ = nullptr;
stub->RemoveDestructionObserver(this);
stub->channel()->scheduler()->DestroySequence(wait_sequence_id_);
}
gpu::CommandBufferStub* stub_;
// Wait tasks are scheduled on our own sequence so that we can't inadvertently
// block the command buffer.
gpu::SequenceId wait_sequence_id_;
// TODO(sandersd): Merge GLES2DecoderHelper implementation into this class.
std::unique_ptr<GLES2DecoderHelper> decoder_helper_;
std::map<GLuint, scoped_refptr<gpu::gles2::TextureRef>> texture_refs_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(CommandBufferHelperImpl);
};
} // namespace
// static
scoped_refptr<CommandBufferHelper> CommandBufferHelper::Create(
gpu::CommandBufferStub* stub) {
return base::MakeRefCounted<CommandBufferHelperImpl>(stub);
}
} // namespace media
// Copyright 2018 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 MEDIA_GPU_COMMAND_BUFFER_HELPER_H_
#define MEDIA_GPU_COMMAND_BUFFER_HELPER_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "media/gpu/media_gpu_export.h"
#include "ui/gl/gl_bindings.h"
namespace gpu {
class CommandBufferStub;
} // namespace gpu
namespace media {
// TODO(sandersd): CommandBufferHelper does not inherently need to be ref
// counted, but some clients want that (VdaVideoDecoder and PictureBufferManager
// both hold a ref to the same CommandBufferHelper). Consider making an owned
// variant.
class MEDIA_GPU_EXPORT CommandBufferHelper
: public base::RefCountedThreadSafe<CommandBufferHelper> {
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
// TODO(sandersd): Consider adding an Initialize(stub) method so that
// CommandBufferHelpers can be created before a stub is available.
static scoped_refptr<CommandBufferHelper> Create(
gpu::CommandBufferStub* stub);
virtual bool MakeContextCurrent() = 0;
// Creates a texture and returns its |service_id|.
//
// See glTexImage2D() for argument definitions.
//
// The texture will be configured as a video frame: linear filtering, clamp to
// edge, no mipmaps. If |target| is GL_TEXTURE_2D, storage will be allocated
// but not initialized.
//
// The context must be current.
//
// TODO(sandersd): Is really necessary to allocate storage? GpuVideoDecoder
// does this, but it's not clear that any clients require it.
virtual GLuint CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) = 0;
// Destroys a texture.
//
// The context must be current.
virtual void DestroyTexture(GLuint service_id) = 0;
// Creates a mailbox for a texture.
//
// TODO(sandersd): Specify the behavior when the stub has been destroyed. The
// current implementation returns an empty (zero) mailbox. One solution would
// be to add a HasStub() method, and not define behavior when it is false.
virtual gpu::Mailbox CreateMailbox(GLuint service_id) = 0;
// Marks layer 0 of the texture as cleared.
virtual void SetCleared(GLuint service_id) = 0;
// Waits for a SyncToken, then runs |done_cb|.
//
// |done_cb| may be destructed without running if the stub is destroyed.
//
// TODO(sandersd): Currently it is possible to lose the stub while
// PictureBufferManager is waiting for all picture buffers, which results in a
// decoding softlock. Notification of wait failure (or just context/stub lost)
// is probably necessary.
virtual void WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) = 0;
protected:
CommandBufferHelper() = default;
// TODO(sandersd): Deleting remaining textures upon destruction requires
// making the context current, which may be undesireable. Consider adding an
// explicit DestroyWithContext() API.
virtual ~CommandBufferHelper() = default;
private:
friend class base::RefCountedThreadSafe<CommandBufferHelper>;
DISALLOW_COPY_AND_ASSIGN(CommandBufferHelper);
};
} // namespace media
#endif // MEDIA_GPU_COMMAND_BUFFER_HELPER_H_
// Copyright 2018 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 "media/gpu/fake_command_buffer_helper.h"
namespace media {
FakeCommandBufferHelper::FakeCommandBufferHelper(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
FakeCommandBufferHelper::~FakeCommandBufferHelper() = default;
void FakeCommandBufferHelper::StubLost() {
DCHECK(task_runner_->BelongsToCurrentThread());
has_stub_ = false;
is_context_lost_ = true;
is_context_current_ = false;
service_ids_.clear();
waits_.clear();
}
void FakeCommandBufferHelper::ContextLost() {
DCHECK(task_runner_->BelongsToCurrentThread());
is_context_lost_ = true;
is_context_current_ = false;
}
void FakeCommandBufferHelper::CurrentContextLost() {
DCHECK(task_runner_->BelongsToCurrentThread());
is_context_current_ = false;
}
bool FakeCommandBufferHelper::HasTexture(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
return service_ids_.count(service_id);
}
void FakeCommandBufferHelper::ReleaseSyncToken(gpu::SyncToken sync_token) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(waits_.count(sync_token));
task_runner_->PostTask(FROM_HERE, std::move(waits_[sync_token]));
waits_.erase(sync_token);
}
bool FakeCommandBufferHelper::MakeContextCurrent() {
DCHECK(task_runner_->BelongsToCurrentThread());
is_context_current_ = !is_context_lost_;
return is_context_current_;
}
GLuint FakeCommandBufferHelper::CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(is_context_current_);
GLuint service_id = next_service_id_++;
service_ids_.insert(service_id);
return service_id;
}
void FakeCommandBufferHelper::DestroyTexture(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(is_context_current_);
DCHECK(service_ids_.count(service_id));
service_ids_.erase(service_id);
}
gpu::Mailbox FakeCommandBufferHelper::CreateMailbox(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(service_ids_.count(service_id));
return gpu::Mailbox::Generate();
}
void FakeCommandBufferHelper::SetCleared(GLuint service_id) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(service_ids_.count(service_id));
}
void FakeCommandBufferHelper::WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!waits_.count(sync_token));
waits_.emplace(sync_token, std::move(done_cb));
}
} // namespace media
// Copyright 2018 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 MEDIA_GPU_FAKE_COMMAND_BUFFER_HELPER_H_
#define MEDIA_GPU_FAKE_COMMAND_BUFFER_HELPER_H_
#include <map>
#include <set>
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "media/gpu/command_buffer_helper.h"
namespace media {
class FakeCommandBufferHelper : public CommandBufferHelper {
public:
explicit FakeCommandBufferHelper(
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
// Signal stub destruction. All textures will be deleted.
void StubLost();
// Signal context loss. MakeContextCurrent() fails after this.
void ContextLost();
// Signal that the context is no longer current.
void CurrentContextLost();
// Complete a pending SyncToken wait.
void ReleaseSyncToken(gpu::SyncToken sync_token);
// Test whether a texture exists (has not been destroyed).
bool HasTexture(GLuint service_id);
// CommandBufferHelper implementation.
bool MakeContextCurrent() override;
GLuint CreateTexture(GLenum target,
GLenum internal_format,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type) override;
void DestroyTexture(GLuint service_id) override;
gpu::Mailbox CreateMailbox(GLuint service_id) override;
void SetCleared(GLuint service_id) override;
void WaitForSyncToken(gpu::SyncToken sync_token,
base::OnceClosure done_cb) override;
private:
~FakeCommandBufferHelper() override;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
bool has_stub_ = true;
bool is_context_lost_ = false;
bool is_context_current_ = false;
GLuint next_service_id_ = 1;
std::set<GLuint> service_ids_;
std::map<gpu::SyncToken, base::OnceClosure> waits_;
DISALLOW_COPY_AND_ASSIGN(FakeCommandBufferHelper);
};
} // namespace media
#endif // MEDIA_GPU_FAKE_COMMAND_BUFFER_HELPER_H_
......@@ -6,7 +6,6 @@
#include <memory>
#include "base/logging.h"
#include "base/macros.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/common/mailbox.h"
......@@ -21,15 +20,7 @@ namespace media {
class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
public:
explicit GLES2DecoderHelperImpl(gpu::DecoderContext* decoder)
: decoder_(decoder) {
DCHECK(decoder_);
gpu::gles2::ContextGroup* group = decoder_->GetContextGroup();
texture_manager_ = group->texture_manager();
mailbox_manager_ = group->mailbox_manager();
// TODO(sandersd): Support GLES2DecoderPassthroughImpl.
DCHECK(texture_manager_);
DCHECK(mailbox_manager_);
}
: decoder_(decoder) {}
bool MakeContextCurrent() override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
......@@ -44,6 +35,10 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
GLenum type) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(decoder_->GetGLContext()->IsCurrent(nullptr));
gpu::gles2::ContextGroup* group = decoder_->GetContextGroup();
gpu::gles2::TextureManager* texture_manager = group->texture_manager();
// TODO(sandersd): Support GLES2DecoderPassthroughImpl.
DCHECK(texture_manager);
// We can't use texture_manager->CreateTexture(), since it requires a unique
// |client_id|. Instead we create the texture directly, and create our own
......@@ -53,39 +48,38 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
glBindTexture(target, texture_id);
scoped_refptr<gpu::gles2::TextureRef> texture_ref =
gpu::gles2::TextureRef::Create(texture_manager_, 0, texture_id);
texture_manager_->SetTarget(texture_ref.get(), target);
texture_manager_->SetLevelInfo(texture_ref.get(), // ref
target, // target
0, // level
internal_format, // internal_format
width, // width
height, // height
1, // depth
0, // border
format, // format
type, // type
gfx::Rect()); // cleared_rect
texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_BASE_LEVEL,
0);
texture_manager_->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MAX_LEVEL, 0);
gpu::gles2::TextureRef::Create(texture_manager, 0, texture_id);
texture_manager->SetTarget(texture_ref.get(), target);
texture_manager->SetLevelInfo(texture_ref.get(), // ref
target, // target
0, // level
internal_format, // internal_format
width, // width
height, // height
1, // depth
0, // border
format, // format
type, // type
gfx::Rect()); // cleared_rect
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_BASE_LEVEL, 0);
texture_manager->SetParameteri(__func__, decoder_->GetErrorState(),
texture_ref.get(), GL_TEXTURE_MAX_LEVEL, 0);
// TODO(sandersd): Do we always want to allocate for GL_TEXTURE_2D?
if (target == GL_TEXTURE_2D) {
......@@ -104,23 +98,17 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
return texture_ref;
}
void SetCleared(gpu::gles2::TextureRef* texture_ref) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
texture_manager_->SetLevelCleared(
texture_ref, texture_ref->texture()->target(), 0, true);
}
gpu::Mailbox CreateMailbox(gpu::gles2::TextureRef* texture_ref) override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
gpu::gles2::ContextGroup* group = decoder_->GetContextGroup();
gpu::MailboxManager* mailbox_manager = group->mailbox_manager();
gpu::Mailbox mailbox = gpu::Mailbox::Generate();
mailbox_manager_->ProduceTexture(mailbox, texture_ref->texture());
mailbox_manager->ProduceTexture(mailbox, texture_ref->texture());
return mailbox;
}
private:
gpu::DecoderContext* decoder_;
gpu::gles2::TextureManager* texture_manager_;
gpu::MailboxManager* mailbox_manager_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(GLES2DecoderHelperImpl);
......@@ -129,6 +117,8 @@ class GLES2DecoderHelperImpl : public GLES2DecoderHelper {
// static
std::unique_ptr<GLES2DecoderHelper> GLES2DecoderHelper::Create(
gpu::DecoderContext* decoder) {
if (!decoder)
return nullptr;
return std::make_unique<GLES2DecoderHelperImpl>(decoder);
}
......
......@@ -50,9 +50,6 @@ class MEDIA_GPU_EXPORT GLES2DecoderHelper {
GLenum format,
GLenum type) = 0;
// Sets the cleared flag on level 0 of the texture.
virtual void SetCleared(gpu::gles2::TextureRef* texture_ref) = 0;
// Creates a mailbox for a texture.
virtual gpu::Mailbox CreateMailbox(gpu::gles2::TextureRef* texture_ref) = 0;
};
......
......@@ -22,10 +22,6 @@ target(link_target_type, "service") {
"media_gpu_channel.h",
"media_gpu_channel_manager.cc",
"media_gpu_channel_manager.h",
"picture_buffer_manager.cc",
"picture_buffer_manager.h",
"vda_video_decoder.cc",
"vda_video_decoder.h",
]
include_dirs = [ "//third_party/mesa/src/include" ]
......@@ -41,7 +37,6 @@ target(link_target_type, "service") {
"//gpu/command_buffer/service:gles2",
"//gpu/ipc/service",
"//media:media_buildflags",
"//media/gpu",
"//media/gpu:buildflags",
"//media/gpu/ipc/common",
"//third_party/mesa:mesa_headers",
......@@ -56,19 +51,3 @@ target(link_target_type, "service") {
deps += [ "//third_party/webrtc/common_video:common_video" ]
}
}
source_set("unit_tests") {
testonly = true
sources = [
"picture_buffer_manager_unittest.cc",
"vda_video_decoder_unittest.cc",
]
deps = [
":service",
"//base",
"//base/test:test_support",
"//media:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
This diff is collapsed.
// Copyright 2018 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 MEDIA_GPU_IPC_SERVICE_PICTURE_BUFFER_MANAGER_H_
#define MEDIA_GPU_IPC_SERVICE_PICTURE_BUFFER_MANAGER_H_
#include <stdint.h>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/video/picture.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace media {
class PictureBufferManager
: public base::RefCountedThreadSafe<PictureBufferManager> {
public:
REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
using ReusePictureBufferCB = base::RepeatingCallback<void(int32_t)>;
// Creates a PictureBufferManager.
//
// |reuse_picture_buffer_cb|: Called when a picture is returned to the pool
// after its VideoFrame has been destructed.
static scoped_refptr<PictureBufferManager> Create(
ReusePictureBufferCB reuse_picture_buffer_cb);
// Provides access to a CommandBufferHelper. This must be done before calling
// CreatePictureBuffers().
//
// TODO(sandersd): It would be convenient to set this up at creation time.
// Consider changes to CommandBufferHelper that would enable that.
virtual void Initialize(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
scoped_refptr<CommandBufferHelper> command_buffer_helper) = 0;
// Predicts whether the VDA can output a picture without reusing one first.
//
// Implementations should be pessimistic; it is better to incorrectly skip
// preroll than to hang waiting for an output that can never come.
virtual bool CanReadWithoutStalling() = 0;
// Creates and returns a vector of picture buffers, or an empty vector on
// failure.
//
// |count|: Number of picture buffers to create.
// |pixel_format|: Describes the arrangement of image data in the picture's
// textures and is surfaced by VideoFrames.
// |planes|: Number of image planes (textures) in the picture.
// |texture_size|: Size of textures to create.
// |texture_target|: Type of textures to create.
//
// Must be called on the GPU thread.
//
// TODO(sandersd): For many subsampled pixel formats, it doesn't make sense to
// allocate all planes with the same size.
// TODO(sandersd): Surface control over allocation for GL_TEXTURE_2D. Right
// now such textures are allocated as RGBA textures. (Other texture targets
// are not automatically allocated.)
// TODO(sandersd): The current implementation makes the context current.
// Consider requiring that the context is already current.
virtual std::vector<PictureBuffer> CreatePictureBuffers(
uint32_t count,
VideoPixelFormat pixel_format,
uint32_t planes,
gfx::Size texture_size,
uint32_t texture_target) = 0;
// Dismisses a picture buffer from the pool.
//
// A picture buffer may be dismissed even if it is bound to a VideoFrame; its
// backing textures will be maintained until the VideoFrame is destroyed.
//
// Must be called on the GPU thread.
virtual bool DismissPictureBuffer(int32_t picture_buffer_id) = 0;
// Creates and returns a VideoFrame bound to a picture buffer, or nullptr on
// failure.
//
// |picture|: Identifies the picture buffer and provides some metadata about
// the desired binding. Not all Picture features are supported.
// |timestamp|: Presentation timestamp of the VideoFrame.
// |visible_rect|: Visible region of the VideoFrame.
// |natural_size|: Natural size of the VideoFrame.
//
// TODO(sandersd): Specify which Picture features are supported.
virtual scoped_refptr<VideoFrame> CreateVideoFrame(
Picture picture,
base::TimeDelta timestamp,
gfx::Rect visible_rect,
gfx::Size natural_size) = 0;
protected:
PictureBufferManager() = default;
// Must be called on the GPU thread if Initialize() was called.
virtual ~PictureBufferManager() = default;
private:
friend class base::RefCountedThreadSafe<PictureBufferManager>;
DISALLOW_COPY_AND_ASSIGN(PictureBufferManager);
};
} // namespace media
#endif // MEDIA_GPU_IPC_SERVICE_PICTURE_BUFFER_MANAGER_H_
// Copyright 2018 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 <stdint.h>
#include "media/gpu/ipc/service/picture_buffer_manager.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "media/gpu/fake_command_buffer_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
// TODO(sandersd): Should be part of //media, as it is used by
// MojoVideoDecoderService (production code) as well.
class StaticSyncTokenClient : public VideoFrame::SyncTokenClient {
public:
explicit StaticSyncTokenClient(const gpu::SyncToken& sync_token)
: sync_token_(sync_token) {}
void GenerateSyncToken(gpu::SyncToken* sync_token) final {
*sync_token = sync_token_;
}
void WaitSyncToken(const gpu::SyncToken& sync_token) final {}
private:
gpu::SyncToken sync_token_;
DISALLOW_COPY_AND_ASSIGN(StaticSyncTokenClient);
};
} // namespace
class PictureBufferManagerImplTest : public testing::Test {
public:
explicit PictureBufferManagerImplTest() {
// TODO(sandersd): Use a separate thread for the GPU task runner.
cbh_ = base::MakeRefCounted<FakeCommandBufferHelper>(
environment_.GetMainThreadTaskRunner());
pbm_ = PictureBufferManager::Create(reuse_cb_.Get());
}
~PictureBufferManagerImplTest() override {}
protected:
void Initialize() {
pbm_->Initialize(environment_.GetMainThreadTaskRunner(), cbh_);
}
std::vector<PictureBuffer> CreateARGBPictureBuffers(uint32_t count) {
return pbm_->CreatePictureBuffers(count, PIXEL_FORMAT_ARGB, 1,
gfx::Size(320, 240), GL_TEXTURE_2D);
}
PictureBuffer CreateARGBPictureBuffer() {
std::vector<PictureBuffer> picture_buffers = CreateARGBPictureBuffers(1);
DCHECK_EQ(picture_buffers.size(), 1U);
return picture_buffers[0];
}
scoped_refptr<VideoFrame> CreateVideoFrame(int32_t picture_buffer_id) {
return pbm_->CreateVideoFrame(
Picture(picture_buffer_id, // picture_buffer_id
0, // bitstream_buffer_id
gfx::Rect(), // visible_rect (ignored)
gfx::ColorSpace::CreateSRGB(), // color_space
false), // allow_overlay
base::TimeDelta(), // timestamp
gfx::Rect(), // visible_rect
gfx::Size()); // natural_size
}
gpu::SyncToken GenerateSyncToken(scoped_refptr<VideoFrame> video_frame) {
gpu::SyncToken sync_token(gpu::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(1),
next_release_count_++);
StaticSyncTokenClient sync_token_client(sync_token);
video_frame->UpdateReleaseSyncToken(&sync_token_client);
return sync_token;
}
base::test::ScopedTaskEnvironment environment_;
uint64_t next_release_count_ = 1;
testing::StrictMock<
base::MockCallback<PictureBufferManager::ReusePictureBufferCB>>
reuse_cb_;
scoped_refptr<FakeCommandBufferHelper> cbh_;
scoped_refptr<PictureBufferManager> pbm_;
DISALLOW_COPY_AND_ASSIGN(PictureBufferManagerImplTest);
};
TEST_F(PictureBufferManagerImplTest, CreateAndDestroy) {}
TEST_F(PictureBufferManagerImplTest, Initialize) {
Initialize();
}
TEST_F(PictureBufferManagerImplTest, CreatePictureBuffer) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
EXPECT_TRUE(cbh_->HasTexture(pb.client_texture_ids()[0]));
}
TEST_F(PictureBufferManagerImplTest, CreatePictureBuffer_ContextLost) {
Initialize();
cbh_->ContextLost();
std::vector<PictureBuffer> pbs = CreateARGBPictureBuffers(1);
EXPECT_TRUE(pbs.empty());
}
TEST_F(PictureBufferManagerImplTest, ReusePictureBuffer) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
scoped_refptr<VideoFrame> frame = CreateVideoFrame(pb.id());
// Dropping the frame does not immediately trigger reuse.
gpu::SyncToken sync_token = GenerateSyncToken(frame);
frame = nullptr;
environment_.RunUntilIdle();
// Completing the SyncToken wait does.
EXPECT_CALL(reuse_cb_, Run(pb.id()));
cbh_->ReleaseSyncToken(sync_token);
environment_.RunUntilIdle();
}
TEST_F(PictureBufferManagerImplTest, DismissPictureBuffer_Available) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
pbm_->DismissPictureBuffer(pb.id());
// Allocated textures should be deleted soon.
environment_.RunUntilIdle();
EXPECT_FALSE(cbh_->HasTexture(pb.client_texture_ids()[0]));
}
TEST_F(PictureBufferManagerImplTest, DismissPictureBuffer_Output) {
Initialize();
PictureBuffer pb = CreateARGBPictureBuffer();
scoped_refptr<VideoFrame> frame = CreateVideoFrame(pb.id());
pbm_->DismissPictureBuffer(pb.id());
// Allocated textures should not be deleted while the VideoFrame exists.
environment_.RunUntilIdle();
EXPECT_TRUE(cbh_->HasTexture(pb.client_texture_ids()[0]));
// Or after it has been returned.
gpu::SyncToken sync_token = GenerateSyncToken(frame);
frame = nullptr;
environment_.RunUntilIdle();
EXPECT_TRUE(cbh_->HasTexture(pb.client_texture_ids()[0]));
// Until the SyncToken has been waited for. (Reuse callback should not be
// called for a dismissed picture buffer.)
cbh_->ReleaseSyncToken(sync_token);
environment_.RunUntilIdle();
EXPECT_FALSE(cbh_->HasTexture(pb.client_texture_ids()[0]));
}
TEST_F(PictureBufferManagerImplTest, CanReadWithoutStalling) {
// Works before Initialize().
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// True before any picture buffers are allocated.
Initialize();
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// True when a picture buffer is available.
PictureBuffer pb = CreateARGBPictureBuffer();
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// False when all picture buffers are used.
scoped_refptr<VideoFrame> frame = CreateVideoFrame(pb.id());
EXPECT_FALSE(pbm_->CanReadWithoutStalling());
// True once a picture buffer is returned.
frame = nullptr;
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
// True after all picture buffers have been dismissed.
pbm_->DismissPictureBuffer(pb.id());
EXPECT_TRUE(pbm_->CanReadWithoutStalling());
}
} // namespace media
This diff is collapsed.
// Copyright 2018 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 MEDIA_GPU_IPC_SERVICE_VDA_VIDEO_DECODER_H_
#define MEDIA_GPU_IPC_SERVICE_VDA_VIDEO_DECODER_H_
#include <stdint.h>
#include <map>
#include <memory>
#include "base/callback_forward.h"
#include "base/containers/mru_cache.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/shared_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "media/base/video_decoder.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/gpu/ipc/service/picture_buffer_manager.h"
#include "media/video/video_decode_accelerator.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gl/gl_bindings.h"
namespace gpu {
class CommandBufferStub;
} // namespace gpu
namespace media {
// Implements the VideoDecoder interface backed by a VideoDecodeAccelerator.
// This class expects to run in the GPU process via MojoVideoDecoder.
class VdaVideoDecoder : public VideoDecoder,
public VideoDecodeAccelerator::Client {
public:
using GetStubCB = base::RepeatingCallback<gpu::CommandBufferStub*()>;
using AllocateShmCB =
base::RepeatingCallback<std::unique_ptr<base::SharedMemory>(size_t)>;
using CreatePictureBufferManagerCB =
base::OnceCallback<scoped_refptr<PictureBufferManager>(
PictureBufferManager::ReusePictureBufferCB)>;
using CreateCommandBufferHelperCB =
base::OnceCallback<scoped_refptr<CommandBufferHelper>()>;
using CreateVdaCB =
base::OnceCallback<std::unique_ptr<VideoDecodeAccelerator>(
scoped_refptr<CommandBufferHelper>)>;
using GetVdaCapabilitiesCB =
base::RepeatingCallback<VideoDecodeAccelerator::Capabilities()>;
// Creates a VdaVideoDecoder. The returned unique_ptr can be safely upcast to
// unique_ptr<VideoDecoder>.
//
// |get_stub_cb|: Callback to retrieve the CommandBufferStub that should be
// used for allocating textures and mailboxes. This callback will be
// called on the GPU thread.
//
// See VdaVideoDecoder() for other arguments.
static std::unique_ptr<VdaVideoDecoder, std::default_delete<VideoDecoder>>
Create(scoped_refptr<base::SingleThreadTaskRunner> parent_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
GetStubCB get_stub_cb);
// |parent_task_runner|: Task runner that |this| should operate on. All
// methods must be called on |parent_task_runner| (should be the Mojo
// MediaService task runner).
// |gpu_task_runner|: Task runner that |get_stub_cb| and GPU command buffer
// methods must be called on (should be the GPU main thread).
// |create_picture_buffer_manager_cb|: PictureBufferManager factory.
// |create_command_buffer_helper_cb|: CommandBufferHelper factory.
// |create_vda_cb|: VideoDecodeAccelerator factory.
// |get_vda_capabilities_cb|: VideDecodeAccelerator::Capabilities provider.
VdaVideoDecoder(
scoped_refptr<base::SingleThreadTaskRunner> parent_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
CreatePictureBufferManagerCB create_picture_buffer_manager_cb,
CreateCommandBufferHelperCB create_command_buffer_helper_cb,
CreateVdaCB create_vda_cb,
GetVdaCapabilitiesCB get_vda_capabilities_cb);
// media::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::RepeatingClosure& reset_cb) override;
bool NeedsBitstreamConversion() const override;
bool CanReadWithoutStalling() const override;
int GetMaxDecodeRequests() const override;
private:
void Destroy() override;
protected:
// Owners should call Destroy(). This is automatic via
// std::default_delete<media::VideoDecoder> when held by a
// std::unique_ptr<media::VideoDecoder>.
~VdaVideoDecoder() override;
private:
// media::VideoDecodeAccelerator::Client implementation.
void NotifyInitializationComplete(bool success) override;
void ProvidePictureBuffers(uint32_t requested_num_of_buffers,
VideoPixelFormat format,
uint32_t textures_per_buffer,
const gfx::Size& dimensions,
uint32_t texture_target) override;
void DismissPictureBuffer(int32_t picture_buffer_id) override;
void PictureReady(const Picture& picture) override;
void NotifyEndOfBitstreamBuffer(int32_t bitstream_buffer_id) override;
void NotifyFlushDone() override;
void NotifyResetDone() override;
void NotifyError(VideoDecodeAccelerator::Error error) override;
// Tasks and thread hopping.
void DestroyOnGpuThread();
void InitializeOnGpuThread();
void InitializeDone(bool status);
void DecodeOnGpuThread(BitstreamBuffer bitstream_buffer);
void PictureReadyOnParentThread(Picture picture);
void NotifyEndOfBitstreamBufferOnParentThread(int32_t bitstream_buffer_id);
void NotifyFlushDoneOnParentThread();
void NotifyResetDoneOnParentThread();
void ProvidePictureBuffersAsync(uint32_t count,
VideoPixelFormat pixel_format,
uint32_t planes,
gfx::Size texture_size,
GLenum texture_target);
void ReusePictureBuffer(int32_t picture_buffer_id);
// Error handling.
void EnterErrorState();
void DestroyCallbacks();
//
// Constant after construction, safe to read on any thread.
//
scoped_refptr<base::SingleThreadTaskRunner> parent_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
scoped_refptr<CommandBufferHelper> command_buffer_helper_;
scoped_refptr<PictureBufferManager> picture_buffer_manager_;
CreateCommandBufferHelperCB create_command_buffer_helper_cb_;
CreateVdaCB create_vda_cb_;
GetVdaCapabilitiesCB get_vda_capabilities_cb_;
//
// Parent thread state.
//
bool has_error_ = false;
InitCB init_cb_;
OutputCB output_cb_;
DecodeCB flush_cb_;
base::RepeatingClosure reset_cb_;
int32_t bitstream_buffer_id_ = 0;
std::map<int32_t, DecodeCB> decode_cbs_;
// Records timestamps so that they can be mapped to output pictures. Must be
// large enough to account for any amount of frame reordering.
base::MRUCache<int32_t, base::TimeDelta> timestamps_;
//
// GPU thread state.
//
std::unique_ptr<VideoDecodeAccelerator> vda_;
//
// Shared state.
//
VideoDecoderConfig config_;
//
// Weak pointers, prefixed by bound thread.
//
// |gpu_weak_vda_| is invalidated when the VDA has notified about an error, or
// has been destroyed. It is not valid to call VDA methods in those cases.
base::WeakPtr<VideoDecodeAccelerator> gpu_weak_vda_;
std::unique_ptr<base::WeakPtrFactory<VideoDecodeAccelerator>>
gpu_weak_vda_factory_;
// |gpu_weak_this_| is never explicitly invalidated.
// |parent_weak_this_| is invalidated when the client calls Destroy(), and
// indicates that we should not make any new client callbacks.
base::WeakPtr<VdaVideoDecoder> gpu_weak_this_;
base::WeakPtr<VdaVideoDecoder> parent_weak_this_;
base::WeakPtrFactory<VdaVideoDecoder> gpu_weak_this_factory_;
base::WeakPtrFactory<VdaVideoDecoder> parent_weak_this_factory_;
DISALLOW_COPY_AND_ASSIGN(VdaVideoDecoder);
};
} // namespace media
#endif // MEDIA_GPU_IPC_SERVICE_VDA_VIDEO_DECODER_H_
This diff is collapsed.
......@@ -198,7 +198,7 @@ void MojoVideoDecoderService::Reset(ResetCallback callback) {
return;
}
// Flush the reader so that pending decodes will be dispatched first.
// Flush the reader so that pending decodes will be dispatches first.
mojo_decoder_buffer_reader_->Flush(
base::Bind(&MojoVideoDecoderService::OnReaderFlushed, weak_this_,
base::Passed(&callback)));
......@@ -254,11 +254,6 @@ void MojoVideoDecoderService::OnDecoderOutput(
DCHECK(client_);
DCHECK(decoder_);
// All MojoVideoDecoder-based decoders are hardware decoders. If you're the
// first to implement an out-of-process decoder that is not power efficent,
// you can remove this DCHECK.
DCHECK(frame->metadata()->IsTrue(VideoFrameMetadata::POWER_EFFICIENT));
base::Optional<base::UnguessableToken> release_token;
if (frame->HasReleaseMailboxCB() && video_frame_handle_releaser_) {
// |video_frame_handle_releaser_| is explicitly constructed with a
......
......@@ -116,12 +116,10 @@ class MockVideoDecoder : public VideoDecoder {
if (!buffer->end_of_stream()) {
gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
mailbox_holders[0].mailbox.name[0] = 1;
scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTextures(
output_cb_.Run(VideoFrame::WrapNativeTextures(
PIXEL_FORMAT_ARGB, mailbox_holders, GetReleaseMailboxCB(),
config_.coded_size(), config_.visible_rect(), config_.natural_size(),
buffer->timestamp());
frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true);
output_cb_.Run(frame);
buffer->timestamp()));
}
// |decode_cb| must not be called from the same stack.
base::ThreadTaskRunnerHandle::Get()->PostTask(
......
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