tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ExternalTexture.h (13741B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #ifndef ExternalTexture_H_
      7 #define ExternalTexture_H_
      8 
      9 #include <array>
     10 
     11 #include "ObjectModel.h"
     12 #include "mozilla/HashTable.h"
     13 #include "mozilla/Span.h"
     14 #include "mozilla/WeakPtr.h"
     15 #include "mozilla/gfx/Types.h"
     16 #include "mozilla/webgpu/WebGPUTypes.h"
     17 #include "mozilla/webgpu/ffi/wgpu.h"
     18 #include "nsIGlobalObject.h"
     19 #include "nsTArrayForwardDeclare.h"
     20 
     21 namespace mozilla {
     22 namespace dom {
     23 struct GPUExternalTextureDescriptor;
     24 class HTMLVideoElement;
     25 class OwningHTMLVideoElementOrVideoFrame;
     26 enum class PredefinedColorSpace : uint8_t;
     27 class VideoFrame;
     28 }  // namespace dom
     29 namespace layers {
     30 class BufferDescriptor;
     31 class DXGITextureHostD3D11;
     32 class DXGIYCbCrTextureHostD3D11;
     33 class Image;
     34 class MacIOSurfaceTextureHostOGL;
     35 }  // namespace layers
     36 
     37 namespace webgpu {
     38 
     39 class Device;
     40 class ExternalTextureSourceClient;
     41 class WebGPUParent;
     42 
     43 // Implementation of WebGPU's GPUExternalTexture [1].
     44 //
     45 // A GPUExternalTexture is a sampleable 2D texture wrapping an external video
     46 // frame. It is an immutable snapshot; its contents may not change over time,
     47 // either from inside WebGPU (it is only sampleable) or from outside WebGPU
     48 // (e.g. due to video frame advancement).
     49 //
     50 // External textures can be imported from either a HTMLVideoElement or a
     51 // VideoFrame, and they can be bound to bind groups. They can be used in WGSL
     52 // shaders via the `texture_external` type.
     53 //
     54 // Our implementation differentiates between the imported snapshot of
     55 // the video frame (see `ExternalTextureSourceClient`) and the external texture
     56 // itself (this class). This allows us to efficiently create multiple
     57 // `ExternalTexture`s from the same source.
     58 //
     59 // The external texture holds a strong reference to its external texture
     60 // source, ensuring the source's resources remain alive as long as required
     61 // by any external textures.
     62 //
     63 // [1] https://www.w3.org/TR/webgpu/#gpuexternaltexture
     64 class ExternalTexture final : public nsWrapperCache,
     65                              public ObjectBase,
     66                              public ChildOf<Device>,
     67                              public SupportsWeakPtr {
     68 public:
     69  GPU_DECL_CYCLE_COLLECTION(ExternalTexture)
     70  GPU_DECL_JS_WRAP(ExternalTexture)
     71 
     72  static already_AddRefed<ExternalTexture> Create(
     73      Device* const aParent, const nsString& aLabel,
     74      const RefPtr<ExternalTextureSourceClient>& aSource,
     75      dom::PredefinedColorSpace aColorSpace);
     76 
     77  // Sets the external texture's "expired" state to true. This gets called at
     78  // the end of the task in which the external texture was imported if
     79  // imported from an HTMLVideoElement, and when the video frame is closed if
     80  // imported from a VideoFrame. It is an error to submit a command buffer
     81  // which uses an expired external texture.
     82  void Expire();
     83  bool IsExpired() const { return mIsExpired; }
     84  void Unexpire();
     85  bool IsDestroyed() const { return mIsDestroyed; }
     86 
     87  void OnSubmit(uint64_t aSubmissionIndex);
     88  void OnSubmittedWorkDone(uint64_t aSubmissionIndex);
     89 
     90  RefPtr<ExternalTextureSourceClient> Source() { return mSource; }
     91 
     92 private:
     93  explicit ExternalTexture(Device* const aParent, RawId aId,
     94                           RefPtr<ExternalTextureSourceClient> aSource);
     95  virtual ~ExternalTexture();
     96 
     97  // Destroys the external texture if it is no longer required, i.e. all
     98  // submitted work using the external texture has completed, and the external
     99  // texture has been expired.
    100  void MaybeDestroy();
    101 
    102  // Hold a strong reference to the source to ensure it stays alive as long as
    103  // the external texture may still be used.
    104  RefPtr<ExternalTextureSourceClient> mSource;
    105  bool mIsExpired = false;
    106  bool mIsDestroyed = false;
    107  uint64_t mLastSubmittedIndex = 0;
    108  uint64_t mLastSubmittedWorkDoneIndex = 0;
    109 };
    110 
    111 // A cache of imported external texture sources. This allows, where possible,
    112 // reusing a previously imported external source rather than importing a new
    113 // one. Each source additionally caches which external textures were created
    114 // from it, meaning where possible we can even reuse the external textures
    115 // themselves.
    116 class ExternalTextureCache : public SupportsWeakPtr {
    117 public:
    118  // Get an external texture matching the descriptor. This may reuse an
    119  // existing external texture or create a new one if required. Returns nullptr
    120  // on error. Throws security error if the source is not origin-clean.
    121  RefPtr<ExternalTexture> GetOrCreate(
    122      Device* aDevice, const dom::GPUExternalTextureDescriptor& aDesc,
    123      ErrorResult& aRv);
    124 
    125  // Removes a previously imported external texture source from the cache. This
    126  // *must* be called by the source when it is destroyed.
    127  void RemoveSource(const ExternalTextureSourceClient* aSource);
    128 
    129 private:
    130  // Gets the external texture source previously imported from an
    131  // HTMLVideoElement or a VideoFrame if still valid, otherwise imports a new
    132  // one. Returns nullptr on failure. Throws security error if the source is not
    133  // origin-clean.
    134  RefPtr<ExternalTextureSourceClient> GetOrCreateSource(
    135      Device* aDevice, const dom::OwningHTMLVideoElementOrVideoFrame& aSource,
    136      ErrorResult& aRv);
    137 
    138  // Map of previously imported external texture sources. Keyed by the value of
    139  // `GetSerial()` for the `layers::Image` they were imported from. We store a
    140  // raw pointer to the source to avoid keeping the source alive unnecessarily.
    141  // As a consequence, the source *must* remove itself from the cache when it is
    142  // destroyed.
    143  HashMap<uint32_t, ExternalTextureSourceClient*> mSources;
    144 };
    145 
    146 // The client side of an imported external texture source. This gets imported
    147 // from either an HTMLVideoElement or a VideoFrame. ExternalTextures can then
    148 // be created from a source. It is important to separate the source from the
    149 // external texture as multiple external textures can be created from the same
    150 // source.
    151 // The client side is responsible for creating and destroying the host side.
    152 // Any external texture created from this source must ensure the source remains
    153 // alive as long as it is required by the external texture, by holding a strong
    154 // reference. The source itself retains a strong reference to the layers::Image
    155 // it was imported from, which ensures that the decoder does not attempt to
    156 // reuse the image's underlying resources while the source is still in use.
    157 class ExternalTextureSourceClient final : public ObjectBase {
    158  NS_INLINE_DECL_REFCOUNTING(ExternalTextureSourceClient)
    159 
    160 public:
    161  // Creates an ExternalTextureSourceClient from a video element or video frame.
    162  // Returns nullptr on failure. Throws security error if the source is not
    163  // origin-clean.
    164  static already_AddRefed<ExternalTextureSourceClient> Create(
    165      Device* aDevice, ExternalTextureCache* aCache,
    166      const dom::OwningHTMLVideoElementOrVideoFrame& aSource, ErrorResult& aRv);
    167 
    168  // Hold a strong reference to the image as long as we are alive. If the
    169  // SurfaceDescriptor sent to the host was a SurfaceDescriptorGPUVideo, this
    170  // ensures the remote TextureHost is kept alive until we have imported the
    171  // textures into wgpu. Additionally this prevents the decoder from recycling
    172  // the underlying resource whilst still in use, e.g. decoding a future video
    173  // frame into a texture that is currently being rendered by wgpu. When all
    174  // external textures created from this source have been destroyed the final
    175  // reference to the source will be released, causing this reference to be
    176  // released, indicating to the decoder that it can reuse the resources.
    177  const RefPtr<layers::Image> mImage;
    178 
    179  // External texture sources can consist of up to 3 planes of texture data, but
    180  // on the client side we do not know how many planes will actually be
    181  // required. We therefore unconditionally make IDs for 3 textures and 3
    182  // texture views, and the host side will only use the IDs that it requires.
    183  const std::array<RawId, 3> mTextureIds;
    184  const std::array<RawId, 3> mViewIds;
    185 
    186  // Get an external texture from this source matching the descriptor. This may
    187  // reuse an existing external texture or create a new one if required.
    188  RefPtr<ExternalTexture> GetOrCreateExternalTexture(
    189      Device* aDevice, const dom::GPUExternalTextureDescriptor& aDesc);
    190 
    191 private:
    192  ExternalTextureSourceClient(WebGPUChild* aChild, RawId aId,
    193                              ExternalTextureCache* aCache,
    194                              const RefPtr<layers::Image>& aImage,
    195                              const std::array<RawId, 3>& aTextureIds,
    196                              const std::array<RawId, 3>& aViewIds);
    197  virtual ~ExternalTextureSourceClient();
    198 
    199  // Pointer to the cache this source is stored in. If the cache is still
    200  // valid then the source *must* remove itself from the cache when it is
    201  // destroyed.
    202  const WeakPtr<ExternalTextureCache> mCache;
    203 
    204  // Cache of external textures created from this source. We can ignore the
    205  // label when deciding whether to reuse an external texture, and since the
    206  // cache is owned by the source we can ignore the source field of the
    207  // descriptor too. This leaves just the color space.
    208  HashMap<dom::PredefinedColorSpace, WeakPtr<ExternalTexture>>
    209      mExternalTextures;
    210 };
    211 
    212 // Host side of an external texture source. This is responsible for creating
    213 // and managing the lifecycle of the wgpu textures and texture views created
    214 // from the provided SurfaceDescriptor.
    215 class ExternalTextureSourceHost {
    216 public:
    217  // Creates an external texture source from a descriptor. If this fails it
    218  // will create an external texture source in an error state, which will be
    219  // propagated to any external textures created from it.
    220  static ExternalTextureSourceHost Create(
    221      WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    222      const ExternalTextureSourceDescriptor& aDesc);
    223 
    224  // Texture and TextureView IDs used by the source. These will be a subset of
    225  // the IDs provided by the client in the descriptor.
    226  Span<const RawId> TextureIds() const { return mTextureIds; }
    227  Span<const RawId> ViewIds() const { return mViewIds; }
    228 
    229  // Returns information required to create the wgpu::ExternalTexture that is
    230  // only available to the host side.
    231  ffi::WGPUExternalTextureDescriptorFromSource GetExternalTextureDescriptor(
    232      ffi::WGPUPredefinedColorSpace aDestColorSpace) const;
    233 
    234  // Called prior to submitting commands which read from this external texture
    235  // source. This can be used to wait on a fence, for example. If this returns
    236  // false, the commands must *not* be submitted.
    237  bool OnBeforeQueueSubmit(WebGPUParent* aParent, RawId aDeviceId,
    238                           RawId aQueueId);
    239 
    240 private:
    241  ExternalTextureSourceHost(Span<const RawId> aTextureIds,
    242                            Span<const RawId> aViewIds, gfx::IntSize aSize,
    243                            gfx::SurfaceFormat aFormat,
    244                            gfx::YUVRangedColorSpace aColorSpace,
    245                            const std::array<float, 6>& aSampleTransform,
    246                            const std::array<float, 6>& aLoadTransform);
    247 
    248  static ExternalTextureSourceHost CreateFromBufferDesc(
    249      WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    250      const ExternalTextureSourceDescriptor& aDesc,
    251      const layers::BufferDescriptor& aBufferDesc, Span<uint8_t> aBuffer);
    252  static ExternalTextureSourceHost CreateFromDXGITextureHost(
    253      WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    254      const ExternalTextureSourceDescriptor& aDesc,
    255      const layers::DXGITextureHostD3D11* aTextureHost);
    256  static ExternalTextureSourceHost CreateFromDXGIYCbCrTextureHost(
    257      WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
    258      const ExternalTextureSourceDescriptor& aDesc,
    259      const layers::DXGIYCbCrTextureHostD3D11* aTextureHost);
    260  static ExternalTextureSourceHost CreateFromMacIOSurfaceTextureHost(
    261      WebGPUParent* aParent, RawId aDeviceId,
    262      const ExternalTextureSourceDescriptor& aDesc,
    263      const layers::MacIOSurfaceTextureHostOGL* aTextureHost);
    264 
    265  // Creates an external texture source in an error state that will be
    266  // propagated to any external textures created from it.
    267  static ExternalTextureSourceHost CreateError();
    268 
    269  // These should be const but can't be else we wouldn't be move constructible.
    270  // While we are always provided with 3 texture IDs and 3 view IDs by the
    271  // client, we only store here the IDs that are actually used. For example an
    272  // RGBA format source will only require 1 texture and 1 view. NV12 will
    273  // require 2 views, and either 1 or 2 textures depending on whether the
    274  // platform natively supports NV12 format textures.
    275  AutoTArray<RawId, 3> mTextureIds;
    276  AutoTArray<RawId, 3> mViewIds;
    277  const gfx::IntSize mSize;
    278  const gfx::SurfaceFormat mFormat;
    279  const gfx::YUVRangedColorSpace mColorSpace;
    280  const std::array<float, 6> mSampleTransform;
    281  const std::array<float, 6> mLoadTransform;
    282 
    283 #ifdef XP_WIN
    284  // ID used to obtain the texture's write fence from the
    285  // CompositeProcessD3D11FencesHolderMap. The fence is created by the encoder,
    286  // which will signal the fence with a value of FenceD3D11::mFenceValue when
    287  // it has finished writing to the texture. We must therefore ensure we wait
    288  // for the fence to reach this value prior to reading from the texture. A
    289  // value of Nothing indicates that we do not need to wait at all.
    290  Maybe<layers::CompositeProcessFencesHolderId> mFenceId;
    291 #endif
    292 };
    293 
    294 }  // namespace webgpu
    295 }  // namespace mozilla
    296 
    297 #endif  // GPU_ExternalTexture_H_