Skip to content

Commit 8c62114

Browse files
committed
Bug 1979100 - Add infrastructure for importing video frames into wgpu textures. r=webgpu-reviewers,webidl,teoxoy,smaug
In order to support webgpu's GPUExternalTexture we must first be able to import video frames into wgpu. To do so we introduce the concept of an external texture "source". This consists of between 1 and 3 textures and texture views, in order to support single-plane RGBA or multiplanar YCbCr data. The source is implemented as a distinct concept from the external texture itself so that multiple external textures can be created from the same source. An external texture source has both a client-side and host-side representation. The client side is responsible for managing the lifecycle of the host side. It obtains a SurfaceDescriptor from the current layers::Image of the video element or frame it was imported from, and sends this to the host side. The host side is responsible for creating the wgpu Textures and TextureViews it needs from the SurfaceDescriptor. It also holds various pieces of information derived from the imported frames that will be required to construct the wgpu::ExternalTextureDescriptor required to create a wgpu::ExternalTexture. For now, we always call Image::BuildSurfaceDescriptorBuffer to obtain the surface descriptor, and therefore only have to handle SurfaceDescriptorBuffers. In subsequent patches we will handle GPUVideo surface descriptors to directly import hardware decoded video frames without requiring a readback. Differential Revision: https://phabricator.services.mozilla.com/D259819
1 parent 02e5b2a commit 8c62114

10 files changed

Lines changed: 814 additions & 15 deletions

File tree

dom/webgpu/ExternalTexture.cpp

Lines changed: 430 additions & 0 deletions
Large diffs are not rendered by default.

dom/webgpu/ExternalTexture.h

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,28 @@
66
#ifndef ExternalTexture_H_
77
#define ExternalTexture_H_
88

9+
#include <array>
10+
911
#include "ObjectModel.h"
12+
#include "mozilla/Span.h"
13+
#include "mozilla/gfx/Types.h"
14+
#include "mozilla/webgpu/WebGPUTypes.h"
1015
#include "nsIGlobalObject.h"
16+
#include "nsTArrayForwardDeclare.h"
17+
18+
namespace mozilla {
19+
namespace dom {
20+
class OwningHTMLVideoElementOrVideoFrame;
21+
}
22+
namespace layers {
23+
class BufferDescriptor;
24+
class Image;
25+
} // namespace layers
26+
27+
namespace webgpu {
1128

12-
namespace mozilla::webgpu {
29+
class Device;
30+
class WebGPUParent;
1331

1432
// NOTE: Incomplete. Follow-up to complete implementation is at
1533
// <https://bugzilla.mozilla.org/show_bug.cgi?id=1827116>.
@@ -29,6 +47,96 @@ class ExternalTexture : public ObjectBase {
2947
void Cleanup() {}
3048
};
3149

32-
} // namespace mozilla::webgpu
50+
// The client side of an imported external texture source. This gets imported
51+
// from either an HTMLVideoElement or a VideoFrame. ExternalTextures can then
52+
// be created from a source. It is important to separate the source from the
53+
// external texture as multiple external textures can be created from the same
54+
// source.
55+
// The client side is responsible for creating and destroying the host side.
56+
// Any external texture created from this source must ensure the source remains
57+
// alive as long as it is required by the external texture, by holding a strong
58+
// reference.
59+
class ExternalTextureSourceClient {
60+
NS_INLINE_DECL_REFCOUNTING(ExternalTextureSourceClient)
61+
62+
public:
63+
// Creates an ExternalTextureSourceClient from a video element or video frame.
64+
// Returns nullptr on failure. Throws security error if the source is not
65+
// origin-clean.
66+
static already_AddRefed<ExternalTextureSourceClient> Create(
67+
Device* aDevice, const dom::OwningHTMLVideoElementOrVideoFrame& aSource,
68+
ErrorResult& aRv);
69+
70+
const RawId mId;
71+
72+
// External texture sources can consist of up to 3 planes of texture data, but
73+
// on the client side we do not know how many planes will actually be
74+
// required. We therefore unconditionally make IDs for 3 textures and 3
75+
// texture views, and the host side will only use the IDs that it requires.
76+
const std::array<RawId, 3> mTextureIds;
77+
const std::array<RawId, 3> mViewIds;
78+
79+
private:
80+
ExternalTextureSourceClient(WebGPUChild* aBridge, RawId aId,
81+
const std::array<RawId, 3>& aTextureIds,
82+
const std::array<RawId, 3>& aViewIds);
83+
~ExternalTextureSourceClient();
84+
85+
// Used to free resources on the host side when we are destroyed, if the
86+
// bridge is still valid.
87+
const WeakPtr<WebGPUChild> mBridge;
88+
};
89+
90+
// Host side of an external texture source. This is responsible for creating
91+
// and managing the lifecycle of the wgpu textures and texture views created
92+
// from the provided SurfaceDescriptor.
93+
class ExternalTextureSourceHost {
94+
public:
95+
// Creates an external texture source from a descriptor. If this fails it
96+
// will create an external texture source in an error state, which will be
97+
// propagated to any external textures created from it.
98+
static ExternalTextureSourceHost Create(
99+
WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
100+
const ExternalTextureSourceDescriptor& aDesc);
101+
102+
// Texture and TextureView IDs used by the source. These will be a subset of
103+
// the IDs provided by the client in the descriptor.
104+
Span<const RawId> TextureIds() const { return mTextureIds; }
105+
Span<const RawId> ViewIds() const { return mViewIds; }
106+
107+
private:
108+
ExternalTextureSourceHost(Span<const RawId> aTextureIds,
109+
Span<const RawId> aViewIds, gfx::IntSize aSize,
110+
gfx::SurfaceFormat aFormat,
111+
gfx::YUVRangedColorSpace aColorSpace,
112+
const std::array<float, 6>& aSampleTransform,
113+
const std::array<float, 6>& aLoadTransform);
114+
115+
static ExternalTextureSourceHost CreateFromBufferDesc(
116+
WebGPUParent* aParent, RawId aDeviceId, RawId aQueueId,
117+
const ExternalTextureSourceDescriptor& aDesc,
118+
const layers::BufferDescriptor& aSd, uint8_t* aBuffer);
119+
120+
// Creates an external texture source in an error state that will be
121+
// propagated to any external textures created from it.
122+
static ExternalTextureSourceHost CreateError();
123+
124+
// These should be const but can't be else we wouldn't be move constructible.
125+
// While we are always provided with 3 texture IDs and 3 view IDs by the
126+
// client, we only store here the IDs that are actually used. For example an
127+
// RGBA format source will only require 1 texture and 1 view. NV12 will
128+
// require 2 views, and either 1 or 2 textures depending on whether the
129+
// platform natively supports NV12 format textures.
130+
AutoTArray<RawId, 3> mTextureIds;
131+
AutoTArray<RawId, 3> mViewIds;
132+
const gfx::IntSize mSize;
133+
MOZ_MAYBE_UNUSED const gfx::SurfaceFormat mFormat;
134+
MOZ_MAYBE_UNUSED const gfx::YUVRangedColorSpace mColorSpace;
135+
MOZ_MAYBE_UNUSED const std::array<float, 6> mSampleTransform;
136+
MOZ_MAYBE_UNUSED const std::array<float, 6> mLoadTransform;
137+
};
138+
139+
} // namespace webgpu
140+
} // namespace mozilla
33141

34142
#endif // GPU_ExternalTexture_H_

dom/webgpu/ipc/PWebGPU.ipdl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ using mozilla::dom::GPUErrorFilter from "mozilla/dom/WebGPUBinding.h";
1515
using mozilla::dom::GPURequestAdapterOptions from "mozilla/dom/WebGPUBinding.h";
1616
using mozilla::dom::GPUCommandBufferDescriptor from "mozilla/dom/WebGPUBinding.h";
1717
using mozilla::dom::GPUBufferDescriptor from "mozilla/dom/WebGPUBinding.h";
18+
using mozilla::webgpu::ExternalTextureSourceDescriptor from "mozilla/webgpu/WebGPUTypes.h";
1819
using mozilla::webgpu::PopErrorScopeResult from "mozilla/webgpu/WebGPUTypes.h";
1920
using mozilla::webgpu::WebGPUCompilationMessage from "mozilla/webgpu/WebGPUTypes.h";
2021
[MoveOnly] using mozilla::ipc::MutableSharedMemoryHandle from "mozilla/ipc/SharedMemoryHandle.h";
@@ -42,6 +43,16 @@ async protocol PWebGPU
4243
parent:
4344
async Messages(uint32_t nrOfMessages, ByteBuf serializedMessages, ByteBuf[] dataBuffers, MutableSharedMemoryHandle[] shmems);
4445

46+
// Ideally this would be sent in Messages(), but we need to implement
47+
// SurfaceDescriptor serialization first.
48+
async CreateExternalTextureSource(
49+
RawId deviceId,
50+
RawId queueId,
51+
RawId externalTextureSourceId,
52+
ExternalTextureSourceDescriptor aDesc
53+
);
54+
55+
4556
child:
4657
async ServerMessage(ByteBuf buf);
4758

dom/webgpu/ipc/WebGPUParent.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <unordered_set>
99

10+
#include "ExternalTexture.h"
1011
#include "mozilla/PodOperations.h"
1112
#include "mozilla/ScopeExit.h"
1213
#include "mozilla/dom/WebGPUBinding.h"
@@ -171,6 +172,18 @@ extern void wgpu_server_remove_shared_texture(WGPUWebGPUParentPtr aParent,
171172
parent->RemoveSharedTexture(aId);
172173
}
173174

175+
extern void wgpu_parent_destroy_external_texture_source(
176+
WGPUWebGPUParentPtr aParent, WGPUExternalTextureSourceId aId) {
177+
auto* const parent = static_cast<WebGPUParent*>(aParent);
178+
parent->DestroyExternalTextureSource(aId);
179+
}
180+
181+
extern void wgpu_parent_drop_external_texture_source(
182+
WGPUWebGPUParentPtr aParent, WGPUExternalTextureSourceId aId) {
183+
auto* const parent = static_cast<WebGPUParent*>(aParent);
184+
parent->DropExternalTextureSource(aId);
185+
}
186+
174187
extern void wgpu_server_dealloc_buffer_shmem(WGPUWebGPUParentPtr aParent,
175188
WGPUBufferId aId) {
176189
auto* parent = static_cast<WebGPUParent*>(aParent);
@@ -779,6 +792,28 @@ void WebGPUParent::RemoveSharedTexture(RawId aTextureId) {
779792
}
780793
}
781794

795+
void WebGPUParent::DestroyExternalTextureSource(RawId aSourceId) {
796+
auto it = mExternalTextureSources.find(aSourceId);
797+
if (it != mExternalTextureSources.end()) {
798+
for (const auto textureId : it->second.TextureIds()) {
799+
ffi::wgpu_server_texture_destroy(mContext.get(), textureId);
800+
}
801+
}
802+
}
803+
804+
void WebGPUParent::DropExternalTextureSource(RawId aSourceId) {
805+
auto it = mExternalTextureSources.find(aSourceId);
806+
if (it != mExternalTextureSources.end()) {
807+
for (const auto viewId : it->second.ViewIds()) {
808+
ffi::wgpu_server_texture_view_drop(mContext.get(), viewId);
809+
}
810+
for (const auto textureId : it->second.TextureIds()) {
811+
ffi::wgpu_server_texture_drop(mContext.get(), textureId);
812+
}
813+
mExternalTextureSources.erase(it);
814+
}
815+
}
816+
782817
void WebGPUParent::QueueSubmit(RawId aQueueId, RawId aDeviceId,
783818
Span<const RawId> aCommandBuffers,
784819
Span<const RawId> aTextureIds) {
@@ -1529,6 +1564,18 @@ ipc::IPCResult WebGPUParent::RecvMessages(
15291564
return IPC_OK();
15301565
}
15311566

1567+
ipc::IPCResult WebGPUParent::RecvCreateExternalTextureSource(
1568+
RawId aDeviceId, RawId aQueueId, RawId aExternalTextureSourceId,
1569+
const ExternalTextureSourceDescriptor& aDesc) {
1570+
MOZ_RELEASE_ASSERT(mExternalTextureSources.find(aExternalTextureSourceId) ==
1571+
mExternalTextureSources.end());
1572+
mExternalTextureSources.emplace(
1573+
aExternalTextureSourceId,
1574+
ExternalTextureSourceHost::Create(this, aDeviceId, aQueueId, aDesc));
1575+
1576+
return IPC_OK();
1577+
}
1578+
15321579
void WebGPUParent::DevicePushErrorScope(RawId aDeviceId,
15331580
const dom::GPUErrorFilter aFilter) {
15341581
const auto& itr = mErrorScopeStackByDevice.find(aDeviceId);

dom/webgpu/ipc/WebGPUParent.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "base/timer.h"
1313
#include "mozilla/WeakPtr.h"
1414
#include "mozilla/ipc/SharedMemoryHandle.h"
15+
#include "mozilla/webgpu/ExternalTexture.h"
1516
#include "mozilla/webgpu/PWebGPUParent.h"
1617
#include "mozilla/webgpu/ffi/wgpu.h"
1718
#include "mozilla/webrender/WebRenderAPI.h"
@@ -102,6 +103,9 @@ class WebGPUParent final : public PWebGPUParent, public SupportsWeakPtr {
102103
ipc::ByteBuf&& aSerializedMessages,
103104
nsTArray<ipc::ByteBuf>&& aDataBuffers,
104105
nsTArray<MutableSharedMemoryHandle>&& aShmems);
106+
ipc::IPCResult RecvCreateExternalTextureSource(
107+
RawId aDeviceId, RawId aQueueId, RawId aExternalTextureSourceId,
108+
const ExternalTextureSourceDescriptor& aDesc);
105109
void QueueSubmit(RawId aQueueId, RawId aDeviceId,
106110
Span<const RawId> aCommandBuffers,
107111
Span<const RawId> aTextureIds);
@@ -179,6 +183,10 @@ class WebGPUParent final : public PWebGPUParent, public SupportsWeakPtr {
179183
RefPtr<gfx::FileHandleWrapper> GetDeviceFenceHandle(const RawId aDeviceId);
180184

181185
void RemoveSharedTexture(RawId aTextureId);
186+
187+
void DestroyExternalTextureSource(RawId aId);
188+
void DropExternalTextureSource(RawId aId);
189+
182190
void DeallocBufferShmem(RawId aBufferId);
183191
void PreDeviceDrop(RawId aDeviceId);
184192

@@ -241,6 +249,8 @@ class WebGPUParent final : public PWebGPUParent, public SupportsWeakPtr {
241249
std::unordered_map<ffi::WGPUTextureId, std::shared_ptr<SharedTexture>>
242250
mSharedTextures;
243251

252+
std::unordered_map<RawId, ExternalTextureSourceHost> mExternalTextureSources;
253+
244254
// Store a set of DeviceIds that have been SendDeviceLost. We use this to
245255
// limit each Device to one DeviceLost message.
246256
nsTHashSet<RawId> mLostDeviceIds;

dom/webgpu/ipc/WebGPUTypes.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88

99
#include <cstdint>
1010

11+
#include "mozilla/GfxMessageUtils.h"
1112
#include "mozilla/Maybe.h"
13+
#include "mozilla/ParamTraits_STL.h"
1214
#include "mozilla/dom/BindingDeclarations.h"
15+
#include "mozilla/ipc/IPDLParamTraits.h"
16+
#include "mozilla/layers/LayersSurfaces.h"
1317
#include "nsString.h"
1418

1519
namespace mozilla::dom {
@@ -78,6 +82,42 @@ class StringHelper {
7882
Maybe<NS_ConvertUTF16toUTF8> mNarrow;
7983
};
8084

85+
// Used to create an ExternalTextureSourceHost.
86+
struct ExternalTextureSourceDescriptor {
87+
std::array<RawId, 3> mTextureIds;
88+
std::array<RawId, 3> mViewIds;
89+
layers::SurfaceDescriptor mSurfaceDescriptor;
90+
gfx::IntSize mSize;
91+
std::array<float, 6> mSampleTransform;
92+
std::array<float, 6> mLoadTransform;
93+
};
94+
8195
} // namespace mozilla::webgpu
8296

97+
namespace IPC {
98+
template <>
99+
struct ParamTraits<mozilla::webgpu::ExternalTextureSourceDescriptor> {
100+
using ParamType = mozilla::webgpu::ExternalTextureSourceDescriptor;
101+
102+
static void Write(MessageWriter* aWriter, const ParamType& aParam) {
103+
WriteParam(aWriter, aParam.mTextureIds);
104+
WriteParam(aWriter, aParam.mViewIds);
105+
WriteParam(aWriter, aParam.mSurfaceDescriptor);
106+
WriteParam(aWriter, aParam.mSize);
107+
WriteParam(aWriter, aParam.mSampleTransform);
108+
WriteParam(aWriter, aParam.mLoadTransform);
109+
}
110+
111+
static bool Read(MessageReader* aReader, ParamType* aResult) {
112+
return ReadParam(aReader, &aResult->mTextureIds) &&
113+
ReadParam(aReader, &aResult->mViewIds) &&
114+
ReadParam(aReader, &aResult->mSurfaceDescriptor) &&
115+
ReadParam(aReader, &aResult->mSize) &&
116+
ReadParam(aReader, &aResult->mSampleTransform) &&
117+
ReadParam(aReader, &aResult->mLoadTransform);
118+
}
119+
};
120+
121+
} // namespace IPC
122+
83123
#endif // WEBGPU_TYPES_H_

dom/webidl/WebGPU.webidl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,12 @@ interface GPUExternalTexture {
456456
};
457457
GPUExternalTexture includes GPUObjectBase;
458458

459+
dictionary GPUExternalTextureDescriptor
460+
: GPUObjectDescriptorBase {
461+
required (HTMLVideoElement or VideoFrame) source;
462+
PredefinedColorSpace colorSpace = "srgb";
463+
};
464+
459465
[Func="mozilla::webgpu::Instance::PrefEnabled",
460466
Exposed=(Window, Worker), SecureContext]
461467
interface GPUSampler {

0 commit comments

Comments
 (0)