Commit 7bcda057 authored by Bryan Bernhart's avatar Bryan Bernhart Committed by Chromium LUCI CQ

Use shared images with DXVA decoder

Adds support to bind video textures used for overlays
with the shared image system. This change enables
overlays to be backed by DXVA decoded output textures
by wrapping them in a mailbox per plane.

Bug: 1011555
Change-Id: Ide149097c1331c0bcb3dc99ae67def8bf4768472
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2485682Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Cr-Commit-Position: refs/heads/master@{#842699}
parent 652d8f66
......@@ -13,6 +13,7 @@
#include "base/logging.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/video_util.h"
......@@ -83,13 +84,11 @@ class PictureBufferManagerImpl : public PictureBufferManager {
DCHECK(planes);
DCHECK_LE(planes, static_cast<uint32_t>(VideoFrame::kMaxPlanes));
if (!use_shared_image) {
// TODO(sandersd): Consider requiring that CreatePictureBuffers() is
// called with the context current.
if (!command_buffer_helper_->MakeContextCurrent()) {
DVLOG(1) << "Failed to make context current";
return std::vector<PictureBuffer>();
}
// TODO(sandersd): Consider requiring that CreatePictureBuffers() is
// called with the context current.
if (!command_buffer_helper_->MakeContextCurrent()) {
DVLOG(1) << "Failed to make context current";
return std::vector<PictureBuffer>();
}
std::vector<PictureBuffer> picture_buffers;
......@@ -97,25 +96,34 @@ class PictureBufferManagerImpl : public PictureBufferManager {
PictureBufferData picture_data = {pixel_format, texture_size,
use_shared_image};
if (!use_shared_image) {
for (uint32_t j = 0; j < planes; j++) {
// Create a texture for this plane.
GLuint service_id = command_buffer_helper_->CreateTexture(
texture_target, GL_RGBA, texture_size.width(),
texture_size.height(), GL_RGBA, GL_UNSIGNED_BYTE);
DCHECK(service_id);
picture_data.service_ids.push_back(service_id);
// The texture is not cleared yet, but it will be before the VDA
// outputs it. Rather than requiring output to happen on the GPU
// thread, mark the texture as cleared immediately.
command_buffer_helper_->SetCleared(service_id);
// Generate a mailbox while we are still on the GPU thread.
picture_data.mailbox_holders[j] = gpu::MailboxHolder(
command_buffer_helper_->CreateMailbox(service_id),
gpu::SyncToken(), texture_target);
}
for (uint32_t j = 0; j < planes; j++) {
// Use the plane size for texture-backed shared and non-shared images.
// Adjust the size by the subsampling factor.
const size_t width =
VideoFrame::Columns(j, pixel_format, texture_size.width());
const size_t height =
VideoFrame::Rows(j, pixel_format, texture_size.height());
picture_data.texture_sizes.emplace_back(width, height);
// Create a texture for this plane.
// When using shared images, the VDA might not require GL textures to
// exist.
// TODO(crbug.com/1011555): Do not allocate GL textures when unused.
GLuint service_id = command_buffer_helper_->CreateTexture(
texture_target, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE);
DCHECK(service_id);
picture_data.service_ids.push_back(service_id);
// The texture is not cleared yet, but it will be before the VDA
// outputs it. Rather than requiring output to happen on the GPU
// thread, mark the texture as cleared immediately.
command_buffer_helper_->SetCleared(service_id);
// Generate a mailbox while we are still on the GPU thread.
picture_data.mailbox_holders[j] = gpu::MailboxHolder(
command_buffer_helper_->CreateMailbox(service_id), gpu::SyncToken(),
texture_target);
}
// Generate a picture buffer ID and record the picture buffer.
......@@ -132,8 +140,9 @@ class PictureBufferManagerImpl : public PictureBufferManager {
// TODO(sandersd): Refactor the bind image callback to use service IDs so
// that we can get rid of the client IDs altogether.
picture_buffers.emplace_back(
picture_buffer_id, texture_size, picture_data.service_ids,
picture_data.service_ids, texture_target, pixel_format);
picture_buffer_id, texture_size, picture_data.texture_sizes,
picture_data.service_ids, picture_data.service_ids, texture_target,
pixel_format);
}
return picture_buffers;
}
......@@ -216,12 +225,12 @@ class PictureBufferManagerImpl : public PictureBufferManager {
// If this |picture| has a SharedImage, then keep a reference to the
// SharedImage in |picture_buffer_data| and update the gpu::MailboxHolder.
DCHECK_EQ(picture_buffer_data.use_shared_image,
!!picture.scoped_shared_image());
if (auto scoped_shared_image = picture.scoped_shared_image()) {
picture_buffer_data.scoped_shared_image = scoped_shared_image;
picture_buffer_data.mailbox_holders[0] =
scoped_shared_image->GetMailboxHolder();
for (uint32_t i = 0; i < VideoFrame::kMaxPlanes; i++) {
if (auto scoped_shared_image = picture.scoped_shared_image(i)) {
picture_buffer_data.scoped_shared_images.push_back(scoped_shared_image);
picture_buffer_data.mailbox_holders[i] =
scoped_shared_image->GetMailboxHolder();
}
}
// Create and return a VideoFrame for the picture buffer.
......@@ -313,7 +322,6 @@ class PictureBufferManagerImpl : public PictureBufferManager {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
std::vector<GLuint> service_ids;
scoped_refptr<Picture::ScopedSharedImage> scoped_shared_image;
{
base::AutoLock lock(picture_buffers_lock_);
const auto& it = picture_buffers_.find(picture_buffer_id);
......@@ -321,16 +329,9 @@ class PictureBufferManagerImpl : public PictureBufferManager {
DCHECK(it->second.dismissed);
DCHECK(!it->second.IsInUse());
service_ids = std::move(it->second.service_ids);
scoped_shared_image = std::move(it->second.scoped_shared_image);
picture_buffers_.erase(it);
}
// If this PictureBuffer is using a SharedImage, let it fall out of scope.
if (scoped_shared_image) {
DCHECK(service_ids.empty());
return;
}
if (!command_buffer_helper_->MakeContextCurrent())
return;
......@@ -351,7 +352,8 @@ class PictureBufferManagerImpl : public PictureBufferManager {
bool use_shared_image = false;
std::vector<GLuint> service_ids;
gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
scoped_refptr<Picture::ScopedSharedImage> scoped_shared_image;
std::vector<gfx::Size> texture_sizes;
std::vector<scoped_refptr<Picture::ScopedSharedImage>> scoped_shared_images;
bool dismissed = false;
// The same picture buffer can be output from the VDA multiple times
......
......@@ -768,6 +768,10 @@ gpu::SharedImageStub* VdaVideoDecoder::GetSharedImageStub() const {
return command_buffer_helper_->GetSharedImageStub();
}
CommandBufferHelper* VdaVideoDecoder::GetCommandBufferHelper() const {
return command_buffer_helper_.get();
}
void VdaVideoDecoder::NotifyErrorOnParentThread(
VideoDecodeAccelerator::Error error) {
DVLOG(1) << __func__ << "(" << error << ")";
......
......@@ -129,6 +129,7 @@ class VdaVideoDecoder : public VideoDecoder,
void NotifyResetDone() override;
void NotifyError(VideoDecodeAccelerator::Error error) override;
gpu::SharedImageStub* GetSharedImageStub() const override;
CommandBufferHelper* GetCommandBufferHelper() const override;
// Tasks and thread hopping.
static void CleanupOnGpuThread(std::unique_ptr<VdaVideoDecoder>);
......
......@@ -494,6 +494,8 @@ bool EGLStreamPictureBuffer::BindSampleToTexture(
DCHECK_EQ(BOUND, state_);
state_ = IN_CLIENT;
shared_images_.resize(picture_buffer_.service_texture_ids().size());
current_d3d_sample_ = sample;
EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
......
......@@ -68,6 +68,31 @@ class DXVAPictureBuffer {
color_space_ = color_space;
}
const std::vector<scoped_refptr<Picture::ScopedSharedImage>>& shared_images()
const {
return shared_images_;
}
void set_shared_image(
size_t plane,
scoped_refptr<Picture::ScopedSharedImage> shared_image) {
DCHECK(plane < shared_images_.size());
shared_images_[plane] = std::move(shared_image);
}
// Picture buffer data used to create a shared image backing.
const PictureBuffer::TextureIds& service_texture_ids() const {
return picture_buffer_.service_texture_ids();
}
gfx::Size texture_size(size_t plane) {
return picture_buffer_.texture_size(plane);
}
VideoPixelFormat pixel_format() const {
return picture_buffer_.pixel_format();
}
// Returns true if these could in theory be used as an overlay. May
// still be drawn using GL depending on the scene and precise hardware
// support.
......@@ -96,6 +121,8 @@ class DXVAPictureBuffer {
gfx::ColorSpace color_space_;
scoped_refptr<gl::GLImage> gl_image_;
std::vector<scoped_refptr<Picture::ScopedSharedImage>> shared_images_;
DISALLOW_COPY_AND_ASSIGN(DXVAPictureBuffer);
};
......
......@@ -21,6 +21,7 @@
#include "base/atomicops.h"
#include "base/base_paths_win.h"
#include "base/bind.h"
#include "base/bind_post_task.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/file_version_info.h"
......@@ -38,12 +39,19 @@
#include "base/win/scoped_co_mem.h"
#include "base/win/windows_version.h"
#include "build/build_config.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/shared_image_backing_d3d.h"
#include "gpu/command_buffer/service/shared_image_factory.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_preferences.h"
#include "gpu/ipc/service/shared_image_stub.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/base/video_frame.h"
#include "media/base/win/mf_helpers.h"
#include "media/filters/vp9_parser.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/gpu/windows/d3d11_video_device_format_support.h"
#include "media/gpu/windows/dxva_picture_buffer_win.h"
#include "media/gpu/windows/supported_profile_helpers.h"
......@@ -59,6 +67,7 @@
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_fence.h"
#include "ui/gl/gl_image_dxgi.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/gl_switches.h"
......@@ -172,6 +181,36 @@ HRESULT g_last_device_removed_reason;
namespace media {
bool VideoPixelFormatToVizFormat(
VideoPixelFormat pixel_format,
size_t textures_per_picture,
std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes>& texture_formats) {
switch (pixel_format) {
case PIXEL_FORMAT_ARGB:
case PIXEL_FORMAT_XRGB:
case PIXEL_FORMAT_ABGR:
case PIXEL_FORMAT_BGRA:
DCHECK_EQ(textures_per_picture, 1u);
texture_formats[0] =
(pixel_format == PIXEL_FORMAT_ABGR) ? viz::RGBA_8888 : viz::BGRA_8888;
return true;
case PIXEL_FORMAT_NV12:
DCHECK_EQ(textures_per_picture, 2u);
texture_formats[0] = viz::RED_8; // Y
texture_formats[1] = viz::RG_88; // UV
return true;
case PIXEL_FORMAT_P016LE:
// TODO(crbug.com/1011555): P010 formats are not fully supported.
// The required Viz formats (viz::R16_EXT and viz::RG16_EXT) are not yet
// supported.
DCHECK_EQ(textures_per_picture, 2u);
return false;
default: // Unsupported
NOTREACHED();
return false;
}
}
constexpr VideoCodecProfile kSupportedProfiles[] = {
H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH,
VP8PROFILE_ANY, VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE2,
......@@ -1325,6 +1364,13 @@ GLenum DXVAVideoDecodeAccelerator::GetSurfaceInternalFormat() const {
return GL_BGRA_EXT;
}
bool DXVAVideoDecodeAccelerator::SupportsSharedImagePictureBuffers() const {
// Shared image is needed to display overlays which can be used directly
// by the video processor.
// TODO(crbug.com/1011555): Support for non-bind cases.
return GetPictureBufferMechanism() == PictureBufferMechanism::BIND;
}
// static
VideoDecodeAccelerator::SupportedProfiles
DXVAVideoDecodeAccelerator::GetSupportedProfiles(
......@@ -2118,12 +2164,18 @@ void DXVAVideoDecodeAccelerator::NotifyPictureReady(
int input_buffer_id,
const gfx::Rect& visible_rect,
const gfx::ColorSpace& color_space,
bool allow_overlay) {
bool allow_overlay,
std::vector<scoped_refptr<Picture::ScopedSharedImage>> shared_images) {
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
// This task could execute after the decoder has been torn down.
if (GetState() != kUninitialized && client_) {
Picture picture(picture_buffer_id, input_buffer_id, visible_rect,
color_space, allow_overlay);
for (uint32_t i = 0; i < shared_images.size(); i++) {
picture.set_scoped_shared_image(shared_images[i], i);
}
client_->PictureReady(picture);
}
}
......@@ -2591,13 +2643,100 @@ void DXVAVideoDecodeAccelerator::BindPictureBufferToSample(
DCHECK(!output_picture_buffers_.empty());
// BindSampleToTexture configures GLImage with the DX11 output texture.
// The DX11 texture is then accessed through the GLImage to create a shared
// image backing below.
bool result = picture_buffer->BindSampleToTexture(this, sample);
RETURN_AND_NOTIFY_ON_FAILURE(result, "Failed to complete copying surface",
RETURN_AND_NOTIFY_ON_FAILURE(result, "Failed to bind sample to texture",
PLATFORM_FAILURE, );
// Create the DX11 texture backed shared images (texture per plane).
std::vector<scoped_refptr<Picture::ScopedSharedImage>> scoped_shared_images;
if (SupportsSharedImagePictureBuffers()) {
gl::GLImageDXGI* gl_image_dxgi =
gl::GLImageDXGI::FromGLImage(picture_buffer->gl_image().get());
DCHECK(gl_image_dxgi);
const size_t textures_per_picture =
picture_buffer->service_texture_ids().size();
// Get the viz resource format per texture.
std::array<viz::ResourceFormat, VideoFrame::kMaxPlanes> viz_formats;
{
const bool result = VideoPixelFormatToVizFormat(
picture_buffer->pixel_format(), textures_per_picture, viz_formats);
RETURN_AND_NOTIFY_ON_FAILURE(
result, "Could not convert pixel format to viz format",
PLATFORM_FAILURE, );
}
CommandBufferHelper* helper = client_->GetCommandBufferHelper();
DCHECK(helper);
for (uint32_t texture_idx = 0; texture_idx < textures_per_picture;
texture_idx++) {
// Usage flags to allow the display compositor to draw from it, video
// to decode, and allow webgl/canvas access.
constexpr uint32_t shared_image_usage =
gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE | gpu::SHARED_IMAGE_USAGE_GLES2 |
gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_DISPLAY |
gpu::SHARED_IMAGE_USAGE_SCANOUT;
// Create a shared image
// TODO(crbug.com/1011555): Need key shared mutex if shared image is ever
// used by another device.
scoped_refptr<gpu::gles2::TexturePassthrough> gl_texture =
gpu::gles2::TexturePassthrough::CheckedCast(helper->GetTexture(
picture_buffer->service_texture_ids()[texture_idx]));
// Create a new shared image mailbox. The existing mailbox belonging to
// this |picture_buffer| will be updated when the video frame is created.
const auto& mailbox = gpu::Mailbox::GenerateForSharedImage();
auto shared_image = std::make_unique<gpu::SharedImageBackingD3D>(
mailbox, viz_formats[texture_idx],
picture_buffer->texture_size(texture_idx),
picture_buffer->color_space(), kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType, shared_image_usage,
/*swap_chain=*/nullptr, std::move(gl_texture),
picture_buffer->gl_image(),
/*buffer_index=*/0, gl_image_dxgi->texture(),
base::win::ScopedHandle(),
/*dxgi_keyed_mutex=*/nullptr);
// Caller is assumed to provide cleared d3d textures.
shared_image->SetCleared();
gpu::SharedImageStub* shared_image_stub = client_->GetSharedImageStub();
DCHECK(shared_image_stub);
const bool success = shared_image_stub->factory()->RegisterBacking(
std::move(shared_image), /* legacy_mailbox */ true);
if (!success) {
RETURN_AND_NOTIFY_ON_FAILURE(false, "Failed to register shared image",
PLATFORM_FAILURE, );
}
auto destroy_shared_image_callback = base::BindPostTask(
main_thread_task_runner_,
base::BindOnce(
shared_image_stub->GetSharedImageDestructionCallback(mailbox),
gpu::SyncToken()));
// Wrap the factory ref with a scoped shared image. The factory ref
// is used instead of requiring a destruction call-back.
auto scoped_shared_image =
base::MakeRefCounted<Picture::ScopedSharedImage>(
mailbox, GetTextureTarget(),
std::move(destroy_shared_image_callback));
scoped_shared_images.push_back(std::move(scoped_shared_image));
}
}
NotifyPictureReady(
picture_buffer->id(), input_buffer_id, picture_buffer->visible_rect(),
picture_buffer->color_space(), picture_buffer->AllowOverlay());
picture_buffer->color_space(), picture_buffer->AllowOverlay(),
std::move(scoped_shared_images));
{
base::AutoLock lock(decoder_lock_);
......
......@@ -112,6 +112,7 @@ class MEDIA_GPU_EXPORT DXVAVideoDecodeAccelerator
const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner)
override;
GLenum GetSurfaceInternalFormat() const override;
bool SupportsSharedImagePictureBuffers() const override;
static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(
const gpu::GpuPreferences& gpu_preferences,
......@@ -248,7 +249,9 @@ class MEDIA_GPU_EXPORT DXVAVideoDecodeAccelerator
int input_buffer_id,
const gfx::Rect& visible_rect,
const gfx::ColorSpace& color_space,
bool allow_overlay);
bool allow_overlay,
std::vector<scoped_refptr<Picture::ScopedSharedImage>>
shared_images = {});
// Sends pending input buffer processed acks to the client if we don't have
// output samples waiting to be processed.
......
......@@ -53,17 +53,37 @@ PictureBuffer::PictureBuffer(int32_t id,
DCHECK_EQ(client_texture_ids.size(), texture_mailboxes.size());
}
PictureBuffer::PictureBuffer(int32_t id,
const gfx::Size& size,
const TextureSizes& texture_sizes,
const TextureIds& client_texture_ids,
const TextureIds& service_texture_ids,
uint32_t texture_target,
VideoPixelFormat pixel_format)
: id_(id),
size_(size),
texture_sizes_(texture_sizes),
client_texture_ids_(client_texture_ids),
service_texture_ids_(service_texture_ids),
texture_target_(texture_target),
pixel_format_(pixel_format) {
// We either not have client texture ids at all, or if we do, then their
// number must be the same as the number of service texture ids.
DCHECK(client_texture_ids_.empty() ||
client_texture_ids_.size() == service_texture_ids_.size());
}
PictureBuffer::PictureBuffer(const PictureBuffer& other) = default;
PictureBuffer::~PictureBuffer() = default;
gpu::Mailbox PictureBuffer::texture_mailbox(size_t plane) const {
if (plane >= texture_mailboxes_.size()) {
LOG(ERROR) << "No mailbox for plane " << plane;
return gpu::Mailbox();
gfx::Size PictureBuffer::texture_size(size_t plane) const {
if (plane >= texture_sizes_.size()) {
LOG(ERROR) << "Missing texture size for plane " << plane;
return gfx::Size();
}
return texture_mailboxes_[plane];
return texture_sizes_[plane];
}
Picture::Picture(int32_t picture_buffer_id,
......
......@@ -13,6 +13,7 @@
#include "base/memory/ref_counted.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/media_export.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/rect.h"
......@@ -25,6 +26,7 @@ namespace media {
class MEDIA_EXPORT PictureBuffer {
public:
using TextureIds = std::vector<uint32_t>;
using TextureSizes = std::vector<gfx::Size>;
PictureBuffer(int32_t id, const gfx::Size& size);
PictureBuffer(int32_t id,
......@@ -42,6 +44,13 @@ class MEDIA_EXPORT PictureBuffer {
const std::vector<gpu::Mailbox>& texture_mailboxes,
uint32_t texture_target,
VideoPixelFormat pixel_format);
PictureBuffer(int32_t id,
const gfx::Size& size,
const TextureSizes& texture_sizes,
const TextureIds& client_texture_ids,
const TextureIds& service_texture_ids,
uint32_t texture_target,
VideoPixelFormat pixel_format);
PictureBuffer(const PictureBuffer& other);
~PictureBuffer();
......@@ -64,11 +73,12 @@ class MEDIA_EXPORT PictureBuffer {
VideoPixelFormat pixel_format() const { return pixel_format_; }
gpu::Mailbox texture_mailbox(size_t plane) const;
gfx::Size texture_size(size_t plane) const;
private:
int32_t id_;
gfx::Size size_;
TextureSizes texture_sizes_;
TextureIds client_texture_ids_;
TextureIds service_texture_ids_;
std::vector<gpu::Mailbox> texture_mailboxes_;
......@@ -151,11 +161,16 @@ class MEDIA_EXPORT Picture {
}
void set_scoped_shared_image(
scoped_refptr<ScopedSharedImage> scoped_shared_image) {
scoped_shared_image_ = scoped_shared_image;
scoped_refptr<ScopedSharedImage> scoped_shared_image,
uint32_t plane = 0) {
DCHECK(plane < scoped_shared_images_.size());
scoped_shared_images_[plane] = scoped_shared_image;
}
scoped_refptr<ScopedSharedImage> scoped_shared_image() const {
return scoped_shared_image_;
scoped_refptr<ScopedSharedImage> scoped_shared_image(
uint32_t plane = 0) const {
DCHECK(plane < scoped_shared_images_.size());
return scoped_shared_images_[plane];
}
private:
......@@ -168,7 +183,8 @@ class MEDIA_EXPORT Picture {
bool size_changed_;
bool texture_owner_;
bool wants_promotion_hint_;
scoped_refptr<ScopedSharedImage> scoped_shared_image_;
std::array<scoped_refptr<ScopedSharedImage>, VideoFrame::kMaxPlanes>
scoped_shared_images_;
};
} // namespace media
......
......@@ -71,6 +71,11 @@ gpu::SharedImageStub* VideoDecodeAccelerator::Client::GetSharedImageStub()
return nullptr;
}
CommandBufferHelper* VideoDecodeAccelerator::Client::GetCommandBufferHelper()
const {
return nullptr;
}
VideoDecodeAccelerator::~VideoDecodeAccelerator() = default;
void VideoDecodeAccelerator::Decode(scoped_refptr<DecoderBuffer> buffer,
......
......@@ -38,6 +38,8 @@ class SharedImageStub;
namespace media {
class CommandBufferHelper;
// Video decoder interface.
// This interface is extended by the various components that ultimately
// implement the backend of PPB_VideoDecoder_Dev.
......@@ -262,6 +264,10 @@ class MEDIA_EXPORT VideoDecodeAccelerator {
// Default implementation returns nullptr.
virtual gpu::SharedImageStub* GetSharedImageStub() const;
// Return the CommandBufferHelper through which GL passthrough textures may
// be created. Default implementation returns nullptr.
virtual CommandBufferHelper* GetCommandBufferHelper() const;
protected:
virtual ~Client() {}
};
......
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