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_