1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310

media / gpu / windows / d3d11_video_decoder.h [blame]

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_H_
#define MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_H_

#include <list>
#include <vector>

#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_info.h"
#include "gpu/config/gpu_preferences.h"
#include "media/base/callback_registry.h"
#include "media/base/status.h"
#include "media/base/supported_video_decoder_config.h"
#include "media/base/video_decoder.h"
#include "media/base/video_types.h"
#include "media/gpu/command_buffer_helper.h"
#include "media/gpu/media_gpu_export.h"
#include "media/gpu/windows/d3d11_decoder_configurator.h"
#include "media/gpu/windows/d3d11_h264_accelerator.h"
#include "media/gpu/windows/d3d11_status.h"
#include "media/gpu/windows/d3d11_texture_selector.h"
#include "media/gpu/windows/d3d11_video_decoder_client.h"
#include "media/gpu/windows/d3d11_video_decoder_wrapper.h"
#include "media/gpu/windows/d3d11_video_frame_mailbox_release_helper.h"
#include "media/gpu/windows/d3d11_vp9_accelerator.h"
#include "media/gpu/windows/d3d_com_defs.h"

namespace gpu {
class CommandBufferStub;
}  // namespace gpu

namespace media {

class D3D11PictureBuffer;
class D3D11VideoDecoderTest;
class MediaLog;

// Video decoder that uses D3D11 directly.  It is intended that this class will
// run the decoder on whatever thread it lives on.  However, at the moment, it
// only works if it's on the gpu main thread.
class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
                                           public D3D11VideoDecoderClient {
 public:
  enum class D3DVersion { kD3D11, kD3D12 };

  // Callback to get a D3D11/12 device.
  using GetD3DDeviceCB = base::RepeatingCallback<ComUnknown(D3DVersion)>;

  // List of configs that we'll check against when initializing.  This is only
  // needed since GpuMojoMediaClient merges our supported configs with the VDA
  // supported configs.
  using SupportedConfigs = std::vector<SupportedVideoDecoderConfig>;

  // |helper| must be called from |gpu_task_runner|.
  static std::unique_ptr<VideoDecoder> Create(
      scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
      std::unique_ptr<MediaLog> media_log,
      const gpu::GpuPreferences& gpu_preferences,
      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
      base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb,
      GetD3DDeviceCB get_d3d_device_cb,
      SupportedConfigs supported_configs);

  D3D11VideoDecoder(const D3D11VideoDecoder&) = delete;
  D3D11VideoDecoder& operator=(const D3D11VideoDecoder&) = delete;

  // VideoDecoder implementation:
  VideoDecoderType GetDecoderType() const override;
  void Initialize(const VideoDecoderConfig& config,
                  bool low_delay,
                  CdmContext* cdm_context,
                  InitCB init_cb,
                  const OutputCB& output_cb,
                  const WaitingCB& waiting_cb) override;
  void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
  void Reset(base::OnceClosure closure) override;
  bool NeedsBitstreamConversion() const override;
  bool CanReadWithoutStalling() const override;
  int GetMaxDecodeRequests() const override;

  // D3D11VideoDecoderClient implementation.
  D3D11PictureBuffer* GetPicture() override;
  void UpdateTimestamp(D3D11PictureBuffer* picture_buffer) override;
  bool OutputResult(const CodecPicture* picture,
                    D3D11PictureBuffer* picture_buffer) override;
  D3DVideoDecoderWrapper* GetWrapper() override;

  bool ResetD3DVideoDecoder();

  static bool GetD3D11FeatureLevel(
      ComD3D11Device dev,
      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
      D3D_FEATURE_LEVEL* feature_level);

  // Return the set of video decoder configs that we support.
  static std::vector<SupportedVideoDecoderConfig>
  GetSupportedVideoDecoderConfigs(
      const gpu::GpuPreferences& gpu_preferences,
      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
      GetD3DDeviceCB get_d3d_device_cb);

 protected:
  // Owners should call Destroy(). This is automatic via
  // std::default_delete<media::VideoDecoder> when held by a
  // std::unique_ptr<media::VideoDecoder>.
  ~D3D11VideoDecoder() override;

 private:
  friend class D3D11VideoDecoderTest;

  D3D11VideoDecoder(
      scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
      std::unique_ptr<MediaLog> media_log,
      const gpu::GpuPreferences& gpu_preferences,
      const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
      base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>
          get_helper_cb,
      GetD3DDeviceCB get_d3d_device_cb,
      SupportedConfigs supported_configs);

  // Receive |buffer|, that is now unused by the client.
  void ReceivePictureBufferFromClient(scoped_refptr<D3D11PictureBuffer> buffer);

  // Picture buffer and related gpu resource initialization done.
  void PictureBufferGPUResourceInitDone(
      scoped_refptr<D3D11PictureBuffer> buffer);

  // Called when the gpu side of initialization is complete.
  void OnGpuInitComplete(
      bool success,
      D3D11VideoFrameMailboxReleaseHelper::ReleaseMailboxCB release_mailbox_cb);

  // Run the decoder loop.
  void DoDecode();

  // Instantiate |accelerated_video_decoder_| based on the video profile.
  // Returns false if the codec is unsupported.
  bool InitializeAcceleratedDecoder(const VideoDecoderConfig& config);

  // Query the video device for a specific decoder ID.
  bool DeviceHasDecoderID(GUID decoder_guid);

  // 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();

  // Measure the number of picture buffers that are currently unused, and see if
  // it's smaller than the minimum we've seen since we've allocated them.
  void MeasurePictureBufferUsage();

  // Log the current measurement of picture buffer usage, as measured by
  // `MeasurePictureBufferUsage()`, and clear the measurement.  Do nothing,
  // successfully, if no measurement has been made.
  void LogPictureBufferUsage();

  // Log the LUID of the adapter used for decoding.
  void LogDecoderAdapterLUID();

  // Create the D3DVideoDecoderWrapper according to the version level.
  std::unique_ptr<D3DVideoDecoderWrapper> CreateD3DVideoDecoderWrapper(
      D3D11DecoderConfigurator* decoder_configurator,
      uint8_t bit_depth);

  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,

    // A fatal error occurred. A terminal state.
    kError,
  };

  // Enter the kError state. This will fail any pending |init_cb_| and/or
  // pending decodes as well. |opt_decoder_code| can be optionally provided for
  // a more descriptive reason passed back up to the decoder stream rather than
  // just kFailed.
  void NotifyError(
      D3D11Status reason,
      DecoderStatus::Codes opt_decoder_code = DecoderStatus::Codes::kFailed);

  // Posts |status| to any pending initialization or decode callbacks.
  void PostDecoderStatus(DecoderStatus status);

  // Mailbox release helper; which lives on the GPU main thread. Note: This must
  // be ref counted to outlive D3D11VideoDecoder since each output VideoFrame
  // uses it to wait on a SyncToken during mailbox release.
  scoped_refptr<D3D11VideoFrameMailboxReleaseHelper> mailbox_release_helper_;

  // GPU main thread task runner.
  scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;

  // Task runner on which |this| lives.
  scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_;

  gpu::GpuPreferences gpu_preferences_;
  gpu::GpuDriverBugWorkarounds gpu_workarounds_;

  // During init, these will be set.
  VideoDecoderConfig config_;
  InitCB init_cb_;
  OutputCB output_cb_;

  // Callback to be used as a release CB for VideoFrames.  Be sure to
  // base::BindPostTaskToCurrentDefault the closure that it takes.
  D3D11VideoFrameMailboxReleaseHelper::ReleaseMailboxCB release_mailbox_cb_;

  // Right now, this is used both for the video decoder and for display.  In
  // the future, this should only be for the video decoder.  We should use
  // the ANGLE device for display (plus texture sharing, if needed).
  GetD3DDeviceCB get_d3d_device_cb_;

  // These may be accessed from |decoder_task_runner_|, since the angle device
  // is in multi-threaded mode.  Just be sure not to set any global state.
  ComD3D11Device device_;
  ComD3D11DeviceContext device_context_;
  ComD3D11VideoDevice video_device_;

  // D3D11 version on this device.
  D3D_FEATURE_LEVEL usable_feature_level_;

  std::unique_ptr<AcceleratedVideoDecoder> accelerated_video_decoder_;

  std::unique_ptr<D3D11DecoderConfigurator> decoder_configurator_;

  std::unique_ptr<TextureSelector> texture_selector_;

  std::list<std::pair<scoped_refptr<DecoderBuffer>, DecodeCB>>
      input_buffer_queue_;
  scoped_refptr<DecoderBuffer> current_buffer_;
  DecodeCB current_decode_cb_;
  base::TimeDelta current_timestamp_;

  // 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;

  // Profile of the video being decoded.
  VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN;

  // Callback to get a command buffer helper.  Must be called from the gpu
  // main thread only.
  base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> get_helper_cb_;

  // Entire class should be single-sequence.
  SEQUENCE_CHECKER(sequence_checker_);

  SupportedConfigs supported_configs_;

  // Should we use shared handles for WebGPU interop or if using Graphite.
  bool use_shared_handle_ = false;

  // Should we use multiple single textures for the decoder output (true) or one
  // texture with multiple array slices (false)?
  bool use_single_video_decoder_texture_ = false;

  std::unique_ptr<D3DVideoDecoderWrapper> d3d_video_decoder_wrapper_;

  // The currently configured bit depth for the decoder. When this changes we
  // need to recreate the decoder.
  uint8_t bit_depth_ = 8u;

  // The currently configured color space for the decoder. When this changes we
  // need to recreate the decoder.
  VideoColorSpace color_space_;

  // The currently configured chroma sampling format on the accelerator. When
  // this changes we need to recreate the decoder.
  VideoChromaSampling chroma_sampling_ = VideoChromaSampling::k420;

  // If set, this is the minimum number of picture buffers that we've seen
  // since the last time it was logged to UMA that are unused by both the
  // client and the decoder.  If unset, then no measurement has been made.
  std::optional<int> min_unused_buffers_;

  // Picture buffer usage is measured periodically after some number of decodes.
  // This tracks how many until the next measurement.  It's used strictly to
  // rate limit the measurements, so we don't spent too much time counting
  // unused picture buffers.
  int decode_count_until_picture_buffer_measurement_ = 0;

  base::WeakPtrFactory<D3D11VideoDecoder> weak_factory_{this};
};

}  // namespace media

#endif  // MEDIA_GPU_WINDOWS_D3D11_VIDEO_DECODER_H_