Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "DXVA2Manager.h"
8
#include <d3d11.h>
9
#include "D3D9SurfaceImage.h"
10
#include "DriverCrashGuard.h"
11
#include "GfxDriverInfo.h"
12
#include "ImageContainer.h"
13
#include "MFTDecoder.h"
14
#include "MediaTelemetryConstants.h"
15
#include "VideoUtils.h"
16
#include "WMFUtils.h"
17
#include "gfxCrashReporterUtils.h"
18
#include "gfxWindowsPlatform.h"
19
#include "mfapi.h"
20
#include "mozilla/StaticPrefs.h"
21
#include "mozilla/Telemetry.h"
22
#include "mozilla/gfx/DeviceManagerDx.h"
23
#include "mozilla/layers/D3D11ShareHandleImage.h"
24
#include "mozilla/layers/ImageBridgeChild.h"
25
#include "mozilla/layers/TextureD3D11.h"
26
#include "mozilla/layers/TextureForwarder.h"
27
#include "mozilla/mscom/EnsureMTA.h"
28
#include "nsPrintfCString.h"
29
#include "nsThreadUtils.h"
30
31
const CLSID CLSID_VideoProcessorMFT = {
32
0x88753b26,
33
0x5b24,
34
0x49bd,
35
{0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82}};
36
37
const GUID MF_XVP_PLAYBACK_MODE = {
38
0x3c5d293f,
39
0xad67,
40
0x4e29,
41
{0xaf, 0x12, 0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9}};
42
43
DEFINE_GUID(MF_LOW_LATENCY, 0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27,
44
0x27, 0xa0, 0x24, 0xee);
45
46
// R600, R700, Evergreen and Cayman AMD cards. These support DXVA via UVD3 or
47
// earlier, and don't handle 1080p60 well.
48
static const DWORD sAMDPreUVD4[] = {
49
// clang-format off
50
0x9400, 0x9401, 0x9402, 0x9403, 0x9405, 0x940a, 0x940b, 0x940f, 0x94c0, 0x94c1, 0x94c3, 0x94c4, 0x94c5,
51
0x94c6, 0x94c7, 0x94c8, 0x94c9, 0x94cb, 0x94cc, 0x94cd, 0x9580, 0x9581, 0x9583, 0x9586, 0x9587, 0x9588,
52
0x9589, 0x958a, 0x958b, 0x958c, 0x958d, 0x958e, 0x958f, 0x9500, 0x9501, 0x9504, 0x9505, 0x9506, 0x9507,
53
0x9508, 0x9509, 0x950f, 0x9511, 0x9515, 0x9517, 0x9519, 0x95c0, 0x95c2, 0x95c4, 0x95c5, 0x95c6, 0x95c7,
54
0x95c9, 0x95cc, 0x95cd, 0x95ce, 0x95cf, 0x9590, 0x9591, 0x9593, 0x9595, 0x9596, 0x9597, 0x9598, 0x9599,
55
0x959b, 0x9610, 0x9611, 0x9612, 0x9613, 0x9614, 0x9615, 0x9616, 0x9710, 0x9711, 0x9712, 0x9713, 0x9714,
56
0x9715, 0x9440, 0x9441, 0x9442, 0x9443, 0x9444, 0x9446, 0x944a, 0x944b, 0x944c, 0x944e, 0x9450, 0x9452,
57
0x9456, 0x945a, 0x945b, 0x945e, 0x9460, 0x9462, 0x946a, 0x946b, 0x947a, 0x947b, 0x9480, 0x9487, 0x9488,
58
0x9489, 0x948a, 0x948f, 0x9490, 0x9491, 0x9495, 0x9498, 0x949c, 0x949e, 0x949f, 0x9540, 0x9541, 0x9542,
59
0x954e, 0x954f, 0x9552, 0x9553, 0x9555, 0x9557, 0x955f, 0x94a0, 0x94a1, 0x94a3, 0x94b1, 0x94b3, 0x94b4,
60
0x94b5, 0x94b9, 0x68e0, 0x68e1, 0x68e4, 0x68e5, 0x68e8, 0x68e9, 0x68f1, 0x68f2, 0x68f8, 0x68f9, 0x68fa,
61
0x68fe, 0x68c0, 0x68c1, 0x68c7, 0x68c8, 0x68c9, 0x68d8, 0x68d9, 0x68da, 0x68de, 0x68a0, 0x68a1, 0x68a8,
62
0x68a9, 0x68b0, 0x68b8, 0x68b9, 0x68ba, 0x68be, 0x68bf, 0x6880, 0x6888, 0x6889, 0x688a, 0x688c, 0x688d,
63
0x6898, 0x6899, 0x689b, 0x689e, 0x689c, 0x689d, 0x9802, 0x9803, 0x9804, 0x9805, 0x9806, 0x9807, 0x9808,
64
0x9809, 0x980a, 0x9640, 0x9641, 0x9647, 0x9648, 0x964a, 0x964b, 0x964c, 0x964e, 0x964f, 0x9642, 0x9643,
65
0x9644, 0x9645, 0x9649, 0x6720, 0x6721, 0x6722, 0x6723, 0x6724, 0x6725, 0x6726, 0x6727, 0x6728, 0x6729,
66
0x6738, 0x6739, 0x673e, 0x6740, 0x6741, 0x6742, 0x6743, 0x6744, 0x6745, 0x6746, 0x6747, 0x6748, 0x6749,
67
0x674a, 0x6750, 0x6751, 0x6758, 0x6759, 0x675b, 0x675d, 0x675f, 0x6840, 0x6841, 0x6842, 0x6843, 0x6849,
68
0x6850, 0x6858, 0x6859, 0x6760, 0x6761, 0x6762, 0x6763, 0x6764, 0x6765, 0x6766, 0x6767, 0x6768, 0x6770,
69
0x6771, 0x6772, 0x6778, 0x6779, 0x677b, 0x6700, 0x6701, 0x6702, 0x6703, 0x6704, 0x6705, 0x6706, 0x6707,
70
0x6708, 0x6709, 0x6718, 0x6719, 0x671c, 0x671d, 0x671f, 0x9900, 0x9901, 0x9903, 0x9904, 0x9905, 0x9906,
71
0x9907, 0x9908, 0x9909, 0x990a, 0x990b, 0x990c, 0x990d, 0x990e, 0x990f, 0x9910, 0x9913, 0x9917, 0x9918,
72
0x9919, 0x9990, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995, 0x9996, 0x9997, 0x9998, 0x9999, 0x999a, 0x999b,
73
0x999c, 0x999d, 0x99a0, 0x99a2, 0x99a4
74
// clang-format on
75
};
76
77
// List of NVidia Telsa GPU known to have broken NV12 rendering.
78
static const DWORD sNVIDIABrokenNV12[] = {
79
// clang-format off
80
0x0191, 0x0193, 0x0194, 0x0197, 0x019d, 0x019e, // G80
81
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, // G84
82
0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f,
83
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, // G86
84
0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f,
85
0x0410, 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, 0x0608, // G92
86
0x0609, 0x060a, 0x060b, 0x060c, 0x060f, 0x0610, 0x0611, 0x0612, 0x0613, 0x0614,
87
0x0615, 0x0617, 0x0618, 0x0619, 0x061a, 0x061b, 0x061c, 0x061d, 0x061e, 0x061f, // G94
88
0x0621, 0x0622, 0x0623, 0x0625, 0x0626, 0x0627, 0x0628, 0x062a, 0x062b, 0x062c,
89
0x062d, 0x062e, 0x0631, 0x0635, 0x0637, 0x0638, 0x063a,
90
0x0640, 0x0641, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064a, // G96
91
0x064b, 0x064c, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0658, 0x0659,
92
0x065a, 0x065b, 0x065c, 0x065f,
93
0x06e0, 0x06e1, 0x06e2, 0x06e3, 0x06e4, 0x06e6, 0x06e7, 0x06e8, 0x06e9, 0x06ea, // G98
94
0x06eb, 0x06ec, 0x06ef, 0x06f1, 0x06f8, 0x06f9, 0x06fa, 0x06fb, 0x06fd, 0x06ff,
95
0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e6, 0x05e7, 0x05e9, 0x05ea, 0x05eb, 0x05ed, // G200
96
0x05ee, 0x05ef,
97
0x0840, 0x0844, 0x0845, 0x0846, 0x0847, 0x0848, 0x0849, 0x084a, 0x084b, 0x084c, // MCP77
98
0x084d, 0x084f,
99
0x0860, 0x0861, 0x0862, 0x0863, 0x0864, 0x0865, 0x0866, 0x0867, 0x0868, 0x0869, // MCP79
100
0x086a, 0x086c, 0x086d, 0x086e, 0x086f, 0x0870, 0x0871, 0x0872, 0x0873, 0x0874,
101
0x0876, 0x087a, 0x087d, 0x087e, 0x087f,
102
0x0ca0, 0x0ca2, 0x0ca3, 0x0ca2, 0x0ca4, 0x0ca5, 0x0ca7, 0x0ca9, 0x0cac, 0x0caf, // GT215
103
0x0cb0, 0x0cb1, 0x0cbc,
104
0x0a20, 0x0a22, 0x0a23, 0x0a26, 0x0a27, 0x0a28, 0x0a29, 0x0a2a, 0x0a2b, 0x0a2c, // GT216
105
0x0a2d, 0x0a32, 0x0a34, 0x0a35, 0x0a38, 0x0a3c,
106
0x0a60, 0x0a62, 0x0a63, 0x0a64, 0x0a65, 0x0a66, 0x0a67, 0x0a68, 0x0a69, 0x0a6a, // GT218
107
0x0a6c, 0x0a6e, 0x0a6f, 0x0a70, 0x0a71, 0x0a72, 0x0a73, 0x0a74, 0x0a75, 0x0a76,
108
0x0a78, 0x0a7a, 0x0a7c, 0x10c0, 0x10c3, 0x10c5, 0x10d8
109
// clang-format on
110
};
111
112
// The size we use for our synchronization surface.
113
// 16x16 is the size recommended by Microsoft (in the D3D9ExDXGISharedSurf
114
// sample) that works best to avoid driver bugs.
115
static const uint32_t kSyncSurfaceSize = 16;
116
117
namespace mozilla {
118
119
using layers::D3D11RecycleAllocator;
120
using layers::D3D11ShareHandleImage;
121
using layers::D3D9RecycleAllocator;
122
using layers::D3D9SurfaceImage;
123
using layers::Image;
124
using layers::ImageContainer;
125
using namespace layers;
126
using namespace gfx;
127
128
class D3D9DXVA2Manager : public DXVA2Manager {
129
public:
130
D3D9DXVA2Manager();
131
virtual ~D3D9DXVA2Manager();
132
133
HRESULT Init(layers::KnowsCompositor* aKnowsCompositor,
134
nsACString& aFailureReason);
135
136
IUnknown* GetDXVADeviceManager() override;
137
138
// Copies a region (aRegion) of the video frame stored in aVideoSample
139
// into an image which is returned by aOutImage.
140
HRESULT CopyToImage(IMFSample* aVideoSample, const gfx::IntRect& aRegion,
141
Image** aOutImage) override;
142
143
bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
144
145
private:
146
bool CanCreateDecoder(const DXVA2_VideoDesc& aDesc,
147
const float aFramerate) const;
148
149
already_AddRefed<IDirectXVideoDecoder> CreateDecoder(
150
const DXVA2_VideoDesc& aDesc) const;
151
152
RefPtr<IDirect3D9Ex> mD3D9;
153
RefPtr<IDirect3DDevice9Ex> mDevice;
154
RefPtr<IDirect3DDeviceManager9> mDeviceManager;
155
RefPtr<D3D9RecycleAllocator> mTextureClientAllocator;
156
RefPtr<IDirectXVideoDecoderService> mDecoderService;
157
RefPtr<IDirect3DSurface9> mSyncSurface;
158
RefPtr<IDirectXVideoDecoder> mDecoder;
159
GUID mDecoderGUID;
160
UINT32 mResetToken = 0;
161
};
162
163
void GetDXVA2ExtendedFormatFromMFMediaType(IMFMediaType* pType,
164
DXVA2_ExtendedFormat* pFormat) {
165
// Get the interlace mode.
166
MFVideoInterlaceMode interlace = MFVideoInterlaceMode(MFGetAttributeUINT32(
167
pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Unknown));
168
169
if (interlace == MFVideoInterlace_MixedInterlaceOrProgressive) {
170
pFormat->SampleFormat = DXVA2_SampleFieldInterleavedEvenFirst;
171
} else {
172
pFormat->SampleFormat = UINT(interlace);
173
}
174
175
pFormat->VideoChromaSubsampling = MFGetAttributeUINT32(
176
pType, MF_MT_VIDEO_CHROMA_SITING, MFVideoChromaSubsampling_Unknown);
177
pFormat->NominalRange = MFGetAttributeUINT32(pType, MF_MT_VIDEO_NOMINAL_RANGE,
178
MFNominalRange_Unknown);
179
pFormat->VideoTransferMatrix = MFGetAttributeUINT32(
180
pType, MF_MT_YUV_MATRIX, MFVideoTransferMatrix_Unknown);
181
pFormat->VideoLighting = MFGetAttributeUINT32(pType, MF_MT_VIDEO_LIGHTING,
182
MFVideoLighting_Unknown);
183
pFormat->VideoPrimaries = MFGetAttributeUINT32(pType, MF_MT_VIDEO_PRIMARIES,
184
MFVideoPrimaries_Unknown);
185
pFormat->VideoTransferFunction = MFGetAttributeUINT32(
186
pType, MF_MT_TRANSFER_FUNCTION, MFVideoTransFunc_Unknown);
187
}
188
189
HRESULT ConvertMFTypeToDXVAType(IMFMediaType* pType, DXVA2_VideoDesc* pDesc) {
190
ZeroMemory(pDesc, sizeof(*pDesc));
191
192
// The D3D format is the first DWORD of the subtype GUID.
193
GUID subtype = GUID_NULL;
194
HRESULT hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
195
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
196
pDesc->Format = (D3DFORMAT)subtype.Data1;
197
198
UINT32 width = 0;
199
UINT32 height = 0;
200
hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
201
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
202
NS_ENSURE_TRUE(width <= MAX_VIDEO_WIDTH, E_FAIL);
203
NS_ENSURE_TRUE(height <= MAX_VIDEO_HEIGHT, E_FAIL);
204
pDesc->SampleWidth = width;
205
pDesc->SampleHeight = height;
206
207
UINT32 fpsNumerator = 0;
208
UINT32 fpsDenominator = 0;
209
hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, &fpsNumerator,
210
&fpsDenominator);
211
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
212
pDesc->InputSampleFreq.Numerator = fpsNumerator;
213
pDesc->InputSampleFreq.Denominator = fpsDenominator;
214
215
GetDXVA2ExtendedFormatFromMFMediaType(pType, &pDesc->SampleFormat);
216
pDesc->OutputFrameFreq = pDesc->InputSampleFreq;
217
if ((pDesc->SampleFormat.SampleFormat ==
218
DXVA2_SampleFieldInterleavedEvenFirst) ||
219
(pDesc->SampleFormat.SampleFormat ==
220
DXVA2_SampleFieldInterleavedOddFirst)) {
221
pDesc->OutputFrameFreq.Numerator *= 2;
222
}
223
224
return S_OK;
225
}
226
227
static const GUID DXVA2_ModeH264_E = {
228
0x1b81be68,
229
0xa0c7,
230
0x11d3,
231
{0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5}};
232
233
static const GUID DXVA2_Intel_ModeH264_E = {
234
0x604F8E68,
235
0x4951,
236
0x4c54,
237
{0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6}};
238
239
// This tests if a DXVA video decoder can be created for the given media
240
// type/resolution. It uses the same decoder device (DXVA2_ModeH264_E -
241
// DXVA2_ModeH264_VLD_NoFGT) as the H264 decoder MFT provided by windows
242
// (CLSID_CMSH264DecoderMFT) uses, so we can use it to determine if the MFT will
243
// use software fallback or not.
244
bool D3D9DXVA2Manager::SupportsConfig(IMFMediaType* aType, float aFramerate) {
245
MOZ_ASSERT(NS_IsMainThread());
246
DXVA2_VideoDesc desc;
247
HRESULT hr = ConvertMFTypeToDXVAType(aType, &desc);
248
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
249
return CanCreateDecoder(desc, aFramerate);
250
}
251
252
D3D9DXVA2Manager::D3D9DXVA2Manager() {
253
MOZ_COUNT_CTOR(D3D9DXVA2Manager);
254
MOZ_ASSERT(NS_IsMainThread());
255
}
256
257
D3D9DXVA2Manager::~D3D9DXVA2Manager() {
258
MOZ_COUNT_DTOR(D3D9DXVA2Manager);
259
MOZ_ASSERT(NS_IsMainThread());
260
}
261
262
IUnknown* D3D9DXVA2Manager::GetDXVADeviceManager() {
263
MutexAutoLock lock(mLock);
264
return mDeviceManager;
265
}
266
267
HRESULT
268
D3D9DXVA2Manager::Init(layers::KnowsCompositor* aKnowsCompositor,
269
nsACString& aFailureReason) {
270
MOZ_ASSERT(NS_IsMainThread());
271
272
ScopedGfxFeatureReporter reporter("DXVA2D3D9");
273
274
gfx::D3D9VideoCrashGuard crashGuard;
275
if (crashGuard.Crashed()) {
276
NS_WARNING("DXVA2D3D9 crash detected");
277
aFailureReason.AssignLiteral("DXVA2D3D9 crashes detected in the past");
278
return E_FAIL;
279
}
280
281
// Create D3D9Ex.
282
HMODULE d3d9lib = LoadLibraryW(L"d3d9.dll");
283
NS_ENSURE_TRUE(d3d9lib, E_FAIL);
284
decltype(Direct3DCreate9Ex)* d3d9Create =
285
(decltype(Direct3DCreate9Ex)*)GetProcAddress(d3d9lib,
286
"Direct3DCreate9Ex");
287
if (!d3d9Create) {
288
NS_WARNING("Couldn't find Direct3DCreate9Ex symbol in d3d9.dll");
289
aFailureReason.AssignLiteral(
290
"Couldn't find Direct3DCreate9Ex symbol in d3d9.dll");
291
return E_FAIL;
292
}
293
RefPtr<IDirect3D9Ex> d3d9Ex;
294
HRESULT hr = d3d9Create(D3D_SDK_VERSION, getter_AddRefs(d3d9Ex));
295
if (!d3d9Ex) {
296
NS_WARNING("Direct3DCreate9 failed");
297
aFailureReason.AssignLiteral("Direct3DCreate9 failed");
298
return E_FAIL;
299
}
300
301
// Ensure we can do the YCbCr->RGB conversion in StretchRect.
302
// Fail if we can't.
303
hr = d3d9Ex->CheckDeviceFormatConversion(
304
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
305
(D3DFORMAT)MAKEFOURCC('N', 'V', '1', '2'), D3DFMT_X8R8G8B8);
306
if (!SUCCEEDED(hr)) {
307
aFailureReason =
308
nsPrintfCString("CheckDeviceFormatConversion failed with error %X", hr);
309
return hr;
310
}
311
312
// Create D3D9DeviceEx. We pass null HWNDs here even though the documentation
313
// suggests that one of them should not be. At this point in time Chromium
314
// does the same thing for video acceleration.
315
D3DPRESENT_PARAMETERS params = {0};
316
params.BackBufferWidth = 1;
317
params.BackBufferHeight = 1;
318
params.BackBufferFormat = D3DFMT_A8R8G8B8;
319
params.BackBufferCount = 1;
320
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
321
params.hDeviceWindow = nullptr;
322
params.Windowed = TRUE;
323
params.Flags = D3DPRESENTFLAG_VIDEO;
324
325
RefPtr<IDirect3DDevice9Ex> device;
326
hr = d3d9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, nullptr,
327
D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED |
328
D3DCREATE_MIXED_VERTEXPROCESSING,
329
&params, nullptr, getter_AddRefs(device));
330
if (!SUCCEEDED(hr)) {
331
aFailureReason = nsPrintfCString("CreateDeviceEx failed with error %X", hr);
332
return hr;
333
}
334
335
// Ensure we can create queries to synchronize operations between devices.
336
// Without this, when we make a copy of the frame in order to share it with
337
// another device, we can't be sure that the copy has finished before the
338
// other device starts using it.
339
RefPtr<IDirect3DQuery9> query;
340
341
hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query));
342
if (!SUCCEEDED(hr)) {
343
aFailureReason = nsPrintfCString("CreateQuery failed with error %X", hr);
344
return hr;
345
}
346
347
// Create and initialize IDirect3DDeviceManager9.
348
UINT resetToken = 0;
349
RefPtr<IDirect3DDeviceManager9> deviceManager;
350
351
hr = wmf::DXVA2CreateDirect3DDeviceManager9(&resetToken,
352
getter_AddRefs(deviceManager));
353
if (!SUCCEEDED(hr)) {
354
aFailureReason = nsPrintfCString(
355
"DXVA2CreateDirect3DDeviceManager9 failed with error %X", hr);
356
return hr;
357
}
358
hr = deviceManager->ResetDevice(device, resetToken);
359
if (!SUCCEEDED(hr)) {
360
aFailureReason = nsPrintfCString(
361
"IDirect3DDeviceManager9::ResetDevice failed with error %X", hr);
362
return hr;
363
}
364
365
HANDLE deviceHandle;
366
RefPtr<IDirectXVideoDecoderService> decoderService;
367
hr = deviceManager->OpenDeviceHandle(&deviceHandle);
368
if (!SUCCEEDED(hr)) {
369
aFailureReason = nsPrintfCString(
370
"IDirect3DDeviceManager9::OpenDeviceHandle failed with error %X", hr);
371
return hr;
372
}
373
374
hr = deviceManager->GetVideoService(
375
deviceHandle, IID_PPV_ARGS(decoderService.StartAssignment()));
376
deviceManager->CloseDeviceHandle(deviceHandle);
377
if (!SUCCEEDED(hr)) {
378
aFailureReason = nsPrintfCString(
379
"IDirectXVideoDecoderServer::GetVideoService failed with error %X", hr);
380
return hr;
381
}
382
383
UINT deviceCount;
384
GUID* decoderDevices = nullptr;
385
hr = decoderService->GetDecoderDeviceGuids(&deviceCount, &decoderDevices);
386
if (!SUCCEEDED(hr)) {
387
aFailureReason = nsPrintfCString(
388
"IDirectXVideoDecoderServer::GetDecoderDeviceGuids failed with error "
389
"%X",
390
hr);
391
return hr;
392
}
393
394
bool found = false;
395
for (UINT i = 0; i < deviceCount; i++) {
396
if (decoderDevices[i] == DXVA2_ModeH264_E ||
397
decoderDevices[i] == DXVA2_Intel_ModeH264_E) {
398
mDecoderGUID = decoderDevices[i];
399
found = true;
400
break;
401
}
402
}
403
CoTaskMemFree(decoderDevices);
404
405
if (!found) {
406
aFailureReason.AssignLiteral("Failed to find an appropriate decoder GUID");
407
return E_FAIL;
408
}
409
410
D3DADAPTER_IDENTIFIER9 adapter;
411
hr = d3d9Ex->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &adapter);
412
if (!SUCCEEDED(hr)) {
413
aFailureReason = nsPrintfCString(
414
"IDirect3D9Ex::GetAdapterIdentifier failed with error %X", hr);
415
return hr;
416
}
417
418
if ((adapter.VendorId == 0x1022 || adapter.VendorId == 0x1002) &&
419
!StaticPrefs::PDMWMFSkipBlacklist()) {
420
for (const auto& model : sAMDPreUVD4) {
421
if (adapter.DeviceId == model) {
422
mIsAMDPreUVD4 = true;
423
break;
424
}
425
}
426
}
427
428
RefPtr<IDirect3DSurface9> syncSurf;
429
hr = device->CreateRenderTarget(kSyncSurfaceSize, kSyncSurfaceSize,
430
D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, TRUE,
431
getter_AddRefs(syncSurf), NULL);
432
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
433
434
mDecoderService = decoderService;
435
436
mResetToken = resetToken;
437
mD3D9 = d3d9Ex;
438
mDevice = device;
439
mDeviceManager = deviceManager;
440
mSyncSurface = syncSurf;
441
442
if (layers::ImageBridgeChild::GetSingleton()) {
443
// There's no proper KnowsCompositor for ImageBridge currently (and it
444
// implements the interface), so just use that if it's available.
445
mTextureClientAllocator = new D3D9RecycleAllocator(
446
layers::ImageBridgeChild::GetSingleton().get(), mDevice);
447
} else {
448
mTextureClientAllocator =
449
new D3D9RecycleAllocator(aKnowsCompositor, mDevice);
450
}
451
mTextureClientAllocator->SetMaxPoolSize(5);
452
453
Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
454
uint32_t(media::MediaDecoderBackend::WMFDXVA2D3D9));
455
456
reporter.SetSuccessful();
457
458
return S_OK;
459
}
460
461
HRESULT
462
D3D9DXVA2Manager::CopyToImage(IMFSample* aSample, const gfx::IntRect& aRegion,
463
Image** aOutImage) {
464
RefPtr<IMFMediaBuffer> buffer;
465
HRESULT hr = aSample->GetBufferByIndex(0, getter_AddRefs(buffer));
466
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
467
468
RefPtr<IDirect3DSurface9> surface;
469
hr = wmf::MFGetService(buffer, MR_BUFFER_SERVICE, IID_IDirect3DSurface9,
470
getter_AddRefs(surface));
471
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
472
473
RefPtr<D3D9SurfaceImage> image = new D3D9SurfaceImage();
474
hr = image->AllocateAndCopy(mTextureClientAllocator, surface, aRegion);
475
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
476
477
RefPtr<IDirect3DSurface9> sourceSurf = image->GetD3D9Surface();
478
479
// Copy a small rect into our sync surface, and then map it
480
// to block until decoding/color conversion completes.
481
RECT copyRect = {0, 0, kSyncSurfaceSize, kSyncSurfaceSize};
482
hr = mDevice->StretchRect(sourceSurf, &copyRect, mSyncSurface, &copyRect,
483
D3DTEXF_NONE);
484
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
485
486
D3DLOCKED_RECT lockedRect;
487
hr = mSyncSurface->LockRect(&lockedRect, NULL, D3DLOCK_READONLY);
488
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
489
490
hr = mSyncSurface->UnlockRect();
491
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
492
493
image.forget(aOutImage);
494
return S_OK;
495
}
496
497
// Count of the number of DXVAManager's we've created. This is also the
498
// number of videos we're decoding with DXVA. Use on main thread only.
499
static uint32_t sDXVAVideosCount = 0;
500
501
/* static */
502
DXVA2Manager* DXVA2Manager::CreateD3D9DXVA(
503
layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason) {
504
MOZ_ASSERT(NS_IsMainThread());
505
HRESULT hr;
506
507
// DXVA processing takes up a lot of GPU resources, so limit the number of
508
// videos we use DXVA with at any one time.
509
uint32_t dxvaLimit = StaticPrefs::PDMWMFMaxDXVAVideos();
510
511
if (sDXVAVideosCount == dxvaLimit) {
512
aFailureReason.AssignLiteral("Too many DXVA videos playing");
513
return nullptr;
514
}
515
516
nsAutoPtr<D3D9DXVA2Manager> d3d9Manager(new D3D9DXVA2Manager());
517
hr = d3d9Manager->Init(aKnowsCompositor, aFailureReason);
518
if (SUCCEEDED(hr)) {
519
return d3d9Manager.forget();
520
}
521
522
// No hardware accelerated video decoding. :(
523
return nullptr;
524
}
525
526
bool D3D9DXVA2Manager::CanCreateDecoder(const DXVA2_VideoDesc& aDesc,
527
const float aFramerate) const {
528
MOZ_ASSERT(NS_IsMainThread());
529
if (IsUnsupportedResolution(aDesc.SampleWidth, aDesc.SampleHeight,
530
aFramerate)) {
531
return false;
532
}
533
RefPtr<IDirectXVideoDecoder> decoder = CreateDecoder(aDesc);
534
return decoder.get() != nullptr;
535
}
536
537
already_AddRefed<IDirectXVideoDecoder> D3D9DXVA2Manager::CreateDecoder(
538
const DXVA2_VideoDesc& aDesc) const {
539
MOZ_ASSERT(NS_IsMainThread());
540
gfx::D3D9VideoCrashGuard crashGuard;
541
if (crashGuard.Crashed()) {
542
NS_WARNING("DXVA2D3D9 crash detected");
543
return nullptr;
544
}
545
546
UINT configCount;
547
DXVA2_ConfigPictureDecode* configs = nullptr;
548
HRESULT hr = mDecoderService->GetDecoderConfigurations(
549
mDecoderGUID, &aDesc, nullptr, &configCount, &configs);
550
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
551
552
RefPtr<IDirect3DSurface9> surface;
553
hr = mDecoderService->CreateSurface(
554
aDesc.SampleWidth, aDesc.SampleHeight, 0,
555
(D3DFORMAT)MAKEFOURCC('N', 'V', '1', '2'), D3DPOOL_DEFAULT, 0,
556
DXVA2_VideoDecoderRenderTarget, surface.StartAssignment(), NULL);
557
if (!SUCCEEDED(hr)) {
558
CoTaskMemFree(configs);
559
return nullptr;
560
}
561
562
for (UINT i = 0; i < configCount; i++) {
563
RefPtr<IDirectXVideoDecoder> decoder;
564
IDirect3DSurface9* surfaces = surface;
565
hr = mDecoderService->CreateVideoDecoder(mDecoderGUID, &aDesc, &configs[i],
566
&surfaces, 1,
567
decoder.StartAssignment());
568
CoTaskMemFree(configs);
569
return decoder.forget();
570
}
571
572
CoTaskMemFree(configs);
573
return nullptr;
574
}
575
576
class D3D11DXVA2Manager : public DXVA2Manager {
577
public:
578
virtual ~D3D11DXVA2Manager();
579
580
HRESULT Init(layers::KnowsCompositor* aKnowsCompositor,
581
nsACString& aFailureReason, ID3D11Device* aDevice);
582
HRESULT InitInternal(layers::KnowsCompositor* aKnowsCompositor,
583
nsACString& aFailureReason, ID3D11Device* aDevice);
584
585
IUnknown* GetDXVADeviceManager() override;
586
587
// Copies a region (aRegion) of the video frame stored in aVideoSample
588
// into an image which is returned by aOutImage.
589
HRESULT CopyToImage(IMFSample* aVideoSample, const gfx::IntRect& aRegion,
590
Image** aOutImage) override;
591
592
HRESULT CopyToBGRATexture(ID3D11Texture2D* aInTexture,
593
ID3D11Texture2D** aOutTexture) override;
594
595
HRESULT ConfigureForSize(IMFMediaType* aInputType,
596
gfx::YUVColorSpace aColorSpace, uint32_t aWidth,
597
uint32_t aHeight) override;
598
599
bool IsD3D11() override { return true; }
600
601
bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
602
603
private:
604
HRESULT CreateFormatConverter();
605
606
HRESULT CreateOutputSample(RefPtr<IMFSample>& aSample,
607
ID3D11Texture2D* aTexture);
608
609
bool CanCreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc,
610
const float aFramerate) const;
611
612
already_AddRefed<ID3D11VideoDecoder> CreateDecoder(
613
const D3D11_VIDEO_DECODER_DESC& aDesc) const;
614
615
RefPtr<ID3D11Device> mDevice;
616
RefPtr<ID3D11DeviceContext> mContext;
617
RefPtr<IMFDXGIDeviceManager> mDXGIDeviceManager;
618
RefPtr<MFTDecoder> mTransform;
619
RefPtr<D3D11RecycleAllocator> mTextureClientAllocator;
620
RefPtr<ID3D11VideoDecoder> mDecoder;
621
RefPtr<layers::SyncObjectClient> mSyncObject;
622
GUID mDecoderGUID;
623
uint32_t mWidth = 0;
624
uint32_t mHeight = 0;
625
UINT mDeviceManagerToken = 0;
626
RefPtr<IMFMediaType> mInputType;
627
GUID mInputSubType;
628
gfx::YUVColorSpace mYUVColorSpace = gfx::YUVColorSpace::BT601;
629
};
630
631
bool D3D11DXVA2Manager::SupportsConfig(IMFMediaType* aType, float aFramerate) {
632
MOZ_ASSERT(NS_IsMainThread());
633
D3D11_VIDEO_DECODER_DESC desc;
634
desc.Guid = mDecoderGUID;
635
desc.OutputFormat = DXGI_FORMAT_NV12;
636
HRESULT hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &desc.SampleWidth,
637
&desc.SampleHeight);
638
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
639
NS_ENSURE_TRUE(desc.SampleWidth <= MAX_VIDEO_WIDTH, false);
640
NS_ENSURE_TRUE(desc.SampleHeight <= MAX_VIDEO_HEIGHT, false);
641
642
return CanCreateDecoder(desc, aFramerate);
643
}
644
645
D3D11DXVA2Manager::~D3D11DXVA2Manager() {}
646
647
IUnknown* D3D11DXVA2Manager::GetDXVADeviceManager() {
648
MutexAutoLock lock(mLock);
649
return mDXGIDeviceManager;
650
}
651
HRESULT
652
D3D11DXVA2Manager::Init(layers::KnowsCompositor* aKnowsCompositor,
653
nsACString& aFailureReason, ID3D11Device* aDevice) {
654
if (!NS_IsMainThread()) {
655
// DXVA Managers used for full video have to be initialized on the main
656
// thread. Managers initialized off the main thread have to pass a device
657
// and can only be used for color conversion.
658
MOZ_ASSERT(aDevice);
659
return InitInternal(aKnowsCompositor, aFailureReason, aDevice);
660
}
661
662
HRESULT hr;
663
ScopedGfxFeatureReporter reporter("DXVA2D3D11");
664
665
gfx::D3D11VideoCrashGuard crashGuard;
666
if (crashGuard.Crashed()) {
667
NS_WARNING("DXVA2D3D11 crash detected");
668
aFailureReason.AssignLiteral("DXVA2D3D11 crashes detected in the past");
669
return E_FAIL;
670
}
671
672
hr = InitInternal(aKnowsCompositor, aFailureReason, aDevice);
673
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
674
675
if (layers::ImageBridgeChild::GetSingleton() || !aKnowsCompositor) {
676
// There's no proper KnowsCompositor for ImageBridge currently (and it
677
// implements the interface), so just use that if it's available.
678
mTextureClientAllocator = new D3D11RecycleAllocator(
679
layers::ImageBridgeChild::GetSingleton().get(), mDevice,
680
gfx::SurfaceFormat::NV12);
681
682
if (ImageBridgeChild::GetSingleton() &&
683
StaticPrefs::PDMWMFUseSyncTexture() &&
684
mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) {
685
// We use a syncobject to avoid the cost of the mutex lock when
686
// compositing, and because it allows color conversion ocurring directly
687
// from this texture DXVA does not seem to accept IDXGIKeyedMutex textures
688
// as input.
689
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
690
layers::ImageBridgeChild::GetSingleton()
691
->GetTextureFactoryIdentifier()
692
.mSyncHandle,
693
mDevice);
694
}
695
} else {
696
mTextureClientAllocator = new D3D11RecycleAllocator(
697
aKnowsCompositor, mDevice, gfx::SurfaceFormat::NV12);
698
if (StaticPrefs::PDMWMFUseSyncTexture()) {
699
// We use a syncobject to avoid the cost of the mutex lock when
700
// compositing, and because it allows color conversion ocurring directly
701
// from this texture DXVA does not seem to accept IDXGIKeyedMutex textures
702
// as input.
703
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
704
aKnowsCompositor->GetTextureFactoryIdentifier().mSyncHandle, mDevice);
705
}
706
}
707
mTextureClientAllocator->SetMaxPoolSize(5);
708
709
Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
710
uint32_t(media::MediaDecoderBackend::WMFDXVA2D3D11));
711
712
reporter.SetSuccessful();
713
714
return S_OK;
715
}
716
717
HRESULT
718
D3D11DXVA2Manager::InitInternal(layers::KnowsCompositor* aKnowsCompositor,
719
nsACString& aFailureReason,
720
ID3D11Device* aDevice) {
721
HRESULT hr;
722
723
mDevice = aDevice;
724
725
if (!mDevice) {
726
mDevice = gfx::DeviceManagerDx::Get()->CreateDecoderDevice();
727
if (!mDevice) {
728
aFailureReason.AssignLiteral("Failed to create D3D11 device for decoder");
729
return E_FAIL;
730
}
731
}
732
733
RefPtr<ID3D10Multithread> mt;
734
hr = mDevice->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
735
NS_ENSURE_TRUE(SUCCEEDED(hr) && mt, hr);
736
mt->SetMultithreadProtected(TRUE);
737
738
mDevice->GetImmediateContext(getter_AddRefs(mContext));
739
740
hr = wmf::MFCreateDXGIDeviceManager(&mDeviceManagerToken,
741
getter_AddRefs(mDXGIDeviceManager));
742
if (!SUCCEEDED(hr)) {
743
aFailureReason =
744
nsPrintfCString("MFCreateDXGIDeviceManager failed with code %X", hr);
745
return hr;
746
}
747
748
hr = mDXGIDeviceManager->ResetDevice(mDevice, mDeviceManagerToken);
749
if (!SUCCEEDED(hr)) {
750
aFailureReason = nsPrintfCString(
751
"IMFDXGIDeviceManager::ResetDevice failed with code %X", hr);
752
return hr;
753
}
754
755
// The IMFTransform interface used by MFTDecoder is documented to require to
756
// run on an MTA thread.
758
// The main thread (where this function is called) is STA, not MTA.
759
RefPtr<MFTDecoder> mft;
760
mozilla::mscom::EnsureMTA([&]() -> void {
761
mft = new MFTDecoder();
762
hr = mft->Create(CLSID_VideoProcessorMFT);
763
764
if (!SUCCEEDED(hr)) {
765
aFailureReason = nsPrintfCString(
766
"MFTDecoder::Create(CLSID_VideoProcessorMFT) failed with code %X",
767
hr);
768
return;
769
}
770
771
hr = mft->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER,
772
ULONG_PTR(mDXGIDeviceManager.get()));
773
if (!SUCCEEDED(hr)) {
774
aFailureReason = nsPrintfCString(
775
"MFTDecoder::SendMFTMessage(MFT_MESSAGE_"
776
"SET_D3D_MANAGER) failed with code %X",
777
hr);
778
return;
779
}
780
});
781
782
if (!SUCCEEDED(hr)) {
783
return hr;
784
}
785
mTransform = mft;
786
787
RefPtr<ID3D11VideoDevice> videoDevice;
788
hr = mDevice->QueryInterface(
789
static_cast<ID3D11VideoDevice**>(getter_AddRefs(videoDevice)));
790
if (!SUCCEEDED(hr)) {
791
aFailureReason =
792
nsPrintfCString("QI to ID3D11VideoDevice failed with code %X", hr);
793
return hr;
794
}
795
796
bool found = false;
797
UINT profileCount = videoDevice->GetVideoDecoderProfileCount();
798
for (UINT i = 0; i < profileCount; i++) {
799
GUID id;
800
hr = videoDevice->GetVideoDecoderProfile(i, &id);
801
if (SUCCEEDED(hr) &&
802
(id == DXVA2_ModeH264_E || id == DXVA2_Intel_ModeH264_E)) {
803
mDecoderGUID = id;
804
found = true;
805
break;
806
}
807
}
808
if (!found) {
809
aFailureReason.AssignLiteral("Failed to find an appropriate decoder GUID");
810
return E_FAIL;
811
}
812
813
BOOL nv12Support = false;
814
hr = videoDevice->CheckVideoDecoderFormat(&mDecoderGUID, DXGI_FORMAT_NV12,
815
&nv12Support);
816
if (!SUCCEEDED(hr)) {
817
aFailureReason =
818
nsPrintfCString("CheckVideoDecoderFormat failed with code %X", hr);
819
return hr;
820
}
821
if (!nv12Support) {
822
aFailureReason.AssignLiteral("Decoder doesn't support NV12 surfaces");
823
return E_FAIL;
824
}
825
826
RefPtr<IDXGIDevice> dxgiDevice;
827
hr = mDevice->QueryInterface(
828
static_cast<IDXGIDevice**>(getter_AddRefs(dxgiDevice)));
829
if (!SUCCEEDED(hr)) {
830
aFailureReason =
831
nsPrintfCString("QI to IDXGIDevice failed with code %X", hr);
832
return hr;
833
}
834
835
RefPtr<IDXGIAdapter> adapter;
836
hr = dxgiDevice->GetAdapter(adapter.StartAssignment());
837
if (!SUCCEEDED(hr)) {
838
aFailureReason =
839
nsPrintfCString("IDXGIDevice::GetAdapter failed with code %X", hr);
840
return hr;
841
}
842
843
DXGI_ADAPTER_DESC adapterDesc;
844
hr = adapter->GetDesc(&adapterDesc);
845
if (!SUCCEEDED(hr)) {
846
aFailureReason =
847
nsPrintfCString("IDXGIAdapter::GetDesc failed with code %X", hr);
848
return hr;
849
}
850
851
if ((adapterDesc.VendorId == 0x1022 || adapterDesc.VendorId == 0x1002) &&
852
!StaticPrefs::PDMWMFSkipBlacklist()) {
853
for (const auto& model : sAMDPreUVD4) {
854
if (adapterDesc.DeviceId == model) {
855
mIsAMDPreUVD4 = true;
856
break;
857
}
858
}
859
}
860
861
return S_OK;
862
}
863
864
HRESULT
865
D3D11DXVA2Manager::CreateOutputSample(RefPtr<IMFSample>& aSample,
866
ID3D11Texture2D* aTexture) {
867
RefPtr<IMFSample> sample;
868
HRESULT hr = wmf::MFCreateSample(getter_AddRefs(sample));
869
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
870
871
RefPtr<IMFMediaBuffer> buffer;
872
hr = wmf::MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), aTexture, 0,
873
FALSE, getter_AddRefs(buffer));
874
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
875
876
hr = sample->AddBuffer(buffer);
877
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
878
879
aSample = sample;
880
return S_OK;
881
}
882
883
HRESULT
884
D3D11DXVA2Manager::CopyToImage(IMFSample* aVideoSample,
885
const gfx::IntRect& aRegion, Image** aOutImage) {
886
NS_ENSURE_TRUE(aVideoSample, E_POINTER);
887
NS_ENSURE_TRUE(aOutImage, E_POINTER);
888
MOZ_ASSERT(mTextureClientAllocator);
889
890
RefPtr<D3D11ShareHandleImage> image = new D3D11ShareHandleImage(
891
gfx::IntSize(mWidth, mHeight), aRegion, mYUVColorSpace);
892
893
// Retrieve the DXGI_FORMAT for the current video sample.
894
RefPtr<IMFMediaBuffer> buffer;
895
HRESULT hr = aVideoSample->GetBufferByIndex(0, getter_AddRefs(buffer));
896
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
897
898
RefPtr<IMFDXGIBuffer> dxgiBuf;
899
hr = buffer->QueryInterface((IMFDXGIBuffer**)getter_AddRefs(dxgiBuf));
900
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
901
902
RefPtr<ID3D11Texture2D> tex;
903
hr = dxgiBuf->GetResource(__uuidof(ID3D11Texture2D), getter_AddRefs(tex));
904
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
905
906
D3D11_TEXTURE2D_DESC inDesc;
907
tex->GetDesc(&inDesc);
908
909
bool ok = image->AllocateTexture(mTextureClientAllocator, mDevice);
910
NS_ENSURE_TRUE(ok, E_FAIL);
911
912
RefPtr<TextureClient> client =
913
image->GetTextureClient(ImageBridgeChild::GetSingleton().get());
914
NS_ENSURE_TRUE(client, E_FAIL);
915
916
RefPtr<ID3D11Texture2D> texture = image->GetTexture();
917
D3D11_TEXTURE2D_DESC outDesc;
918
texture->GetDesc(&outDesc);
919
920
RefPtr<IDXGIKeyedMutex> mutex;
921
texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
922
923
{
924
AutoTextureLock(mutex, hr, 2000);
925
if (mutex && (FAILED(hr) || hr == WAIT_TIMEOUT || hr == WAIT_ABANDONED)) {
926
return hr;
927
}
928
929
if (!mutex && mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) {
930
NS_ENSURE_TRUE(mSyncObject, E_FAIL);
931
}
932
933
// The D3D11TextureClientAllocator may return a different texture format
934
// than preferred. In which case the destination texture will be BGRA32.
935
if (outDesc.Format == inDesc.Format) {
936
// Our video frame is stored in a non-sharable ID3D11Texture2D. We need
937
// to create a copy of that frame as a sharable resource, save its share
938
// handle, and put that handle into the rendering pipeline.
939
UINT width = std::min(inDesc.Width, outDesc.Width);
940
UINT height = std::min(inDesc.Height, outDesc.Height);
941
D3D11_BOX srcBox = {0, 0, 0, width, height, 1};
942
943
UINT index;
944
dxgiBuf->GetSubresourceIndex(&index);
945
mContext->CopySubresourceRegion(texture, 0, 0, 0, 0, tex, index, &srcBox);
946
} else {
947
// Use MFT to do color conversion.
948
hr = E_FAIL;
949
mozilla::mscom::EnsureMTA(
950
[&]() -> void { hr = mTransform->Input(aVideoSample); });
951
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
952
953
RefPtr<IMFSample> sample;
954
hr = CreateOutputSample(sample, texture);
955
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
956
957
hr = E_FAIL;
958
mozilla::mscom::EnsureMTA(
959
[&]() -> void { hr = mTransform->Output(&sample); });
960
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
961
}
962
}
963
964
if (!mutex && mDevice != DeviceManagerDx::Get()->GetCompositorDevice() &&
965
mSyncObject) {
966
// It appears some race-condition may allow us to arrive here even when
967
// mSyncObject is null. It's better to avoid that crash.
968
client->SyncWithObject(mSyncObject);
969
if (!mSyncObject->Synchronize(true)) {
970
return DXGI_ERROR_DEVICE_RESET;
971
}
972
}
973
974
image.forget(aOutImage);
975
976
return S_OK;
977
}
978
979
HRESULT
980
D3D11DXVA2Manager::CopyToBGRATexture(ID3D11Texture2D* aInTexture,
981
ID3D11Texture2D** aOutTexture) {
982
NS_ENSURE_TRUE(aInTexture, E_POINTER);
983
NS_ENSURE_TRUE(aOutTexture, E_POINTER);
984
985
HRESULT hr;
986
RefPtr<ID3D11Texture2D> texture, inTexture;
987
988
inTexture = aInTexture;
989
990
CD3D11_TEXTURE2D_DESC desc;
991
aInTexture->GetDesc(&desc);
992
993
if (!mInputType || desc.Width != mWidth || desc.Height != mHeight) {
994
RefPtr<IMFMediaType> inputType;
995
hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
996
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
997
998
hr = inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
999
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1000
1001
const GUID subType = [&]() {
1002
switch (desc.Format) {
1003
case DXGI_FORMAT_NV12:
1004
return MFVideoFormat_NV12;
1005
case DXGI_FORMAT_P010:
1006
return MFVideoFormat_P010;
1007
case DXGI_FORMAT_P016:
1008
return MFVideoFormat_P016;
1009
default:
1010
MOZ_ASSERT_UNREACHABLE("Unexpected texture type");
1011
return MFVideoFormat_NV12;
1012
}
1013
}();
1014
1015
hr = inputType->SetGUID(MF_MT_SUBTYPE, subType);
1016
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1017
1018
hr = inputType->SetUINT32(MF_MT_INTERLACE_MODE,
1019
MFVideoInterlace_Progressive);
1020
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1021
1022
hr = inputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
1023
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1024
1025
hr = ConfigureForSize(inputType, mYUVColorSpace, desc.Width, desc.Height);
1026
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1027
}
1028
1029
RefPtr<IDXGIKeyedMutex> mutex;
1030
inTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
1031
// The rest of this function will not work if inTexture implements
1032
// IDXGIKeyedMutex! In that case case we would have to copy to a
1033
// non-mutex using texture.
1034
1035
if (mutex) {
1036
RefPtr<ID3D11Texture2D> newTexture;
1037
1038
desc.MiscFlags = 0;
1039
hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(newTexture));
1040
NS_ENSURE_TRUE(SUCCEEDED(hr) && newTexture, E_FAIL);
1041
1042
hr = mutex->AcquireSync(0, 2000);
1043
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1044
1045
mContext->CopyResource(newTexture, inTexture);
1046
1047
mutex->ReleaseSync(0);
1048
inTexture = newTexture;
1049
}
1050
1051
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
1052
1053
hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
1054
NS_ENSURE_TRUE(SUCCEEDED(hr) && texture, E_FAIL);
1055
1056
RefPtr<IMFSample> inputSample;
1057
wmf::MFCreateSample(getter_AddRefs(inputSample));
1058
1059
// If these aren't set the decoder fails.
1060
inputSample->SetSampleTime(10);
1061
inputSample->SetSampleDuration(10000);
1062
1063
RefPtr<IMFMediaBuffer> inputBuffer;
1064
hr = wmf::MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), inTexture, 0,
1065
FALSE, getter_AddRefs(inputBuffer));
1066
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1067
1068
inputSample->AddBuffer(inputBuffer);
1069
1070
hr = E_FAIL;
1071
mozilla::mscom::EnsureMTA(
1072
[&]() -> void { hr = mTransform->Input(inputSample); });
1073
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1074
1075
RefPtr<IMFSample> outputSample;
1076
hr = CreateOutputSample(outputSample, texture);
1077
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1078
1079
hr = E_FAIL;
1080
mozilla::mscom::EnsureMTA(
1081
[&]() -> void { hr = mTransform->Output(&outputSample); });
1082
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1083
1084
texture.forget(aOutTexture);
1085
1086
return S_OK;
1087
}
1088
1089
HRESULT
1090
D3D11DXVA2Manager::ConfigureForSize(IMFMediaType* aInputType,
1091
gfx::YUVColorSpace aColorSpace,
1092
uint32_t aWidth, uint32_t aHeight) {
1093
GUID subType = {0};
1094
HRESULT hr = aInputType->GetGUID(MF_MT_SUBTYPE, &subType);
1095
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1096
1097
if (subType == mInputSubType && aWidth == mWidth && aHeight == mHeight &&
1098
mYUVColorSpace == aColorSpace) {
1099
// If the media type hasn't changed, don't reconfigure.
1100
return S_OK;
1101
}
1102
1103
// Create a copy of our input type.
1104
RefPtr<IMFMediaType> inputType;
1105
hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
1106
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1107
hr = aInputType->CopyAllItems(inputType);
1108
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1109
1110
hr = MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, aWidth, aHeight);
1111
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1112
1113
RefPtr<IMFAttributes> attr;
1114
mozilla::mscom::EnsureMTA(
1115
[&]() -> void { attr = mTransform->GetAttributes(); });
1116
NS_ENSURE_TRUE(attr != nullptr, E_FAIL);
1117
1118
hr = attr->SetUINT32(MF_XVP_PLAYBACK_MODE, TRUE);
1119
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1120
1121
hr = attr->SetUINT32(MF_LOW_LATENCY, FALSE);
1122
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1123
1124
RefPtr<IMFMediaType> outputType;
1125
hr = wmf::MFCreateMediaType(getter_AddRefs(outputType));
1126
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1127
1128
hr = outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
1129
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1130
1131
hr = outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
1132
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1133
1134
hr = E_FAIL;
1135
mozilla::mscom::EnsureMTA([&]() -> void {
1136
hr = mTransform->SetMediaTypes(
1137
inputType, outputType, [aWidth, aHeight](IMFMediaType* aOutput) {
1138
HRESULT hr = aOutput->SetUINT32(MF_MT_INTERLACE_MODE,
1139
MFVideoInterlace_Progressive);
1140
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1141
hr = aOutput->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
1142
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1143
hr = MFSetAttributeSize(aOutput, MF_MT_FRAME_SIZE, aWidth, aHeight);
1144
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1145
1146
return S_OK;
1147
});
1148
});
1149
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1150
1151
mWidth = aWidth;
1152
mHeight = aHeight;
1153
mInputType = inputType;
1154
mInputSubType = subType;
1155
mYUVColorSpace = aColorSpace;
1156
if (mTextureClientAllocator) {
1157
gfx::SurfaceFormat format = [&]() {
1158
if (subType == MFVideoFormat_NV12) {
1159
return gfx::SurfaceFormat::NV12;
1160
} else if (subType == MFVideoFormat_P010) {
1161
return gfx::SurfaceFormat::P010;
1162
} else if (subType == MFVideoFormat_P016) {
1163
return gfx::SurfaceFormat::P016;
1164
} else {
1165
MOZ_ASSERT_UNREACHABLE("Unexpected texture type");
1166
return gfx::SurfaceFormat::NV12;
1167
}
1168
}();
1169
mTextureClientAllocator->SetPreferredSurfaceFormat(format);
1170
}
1171
return S_OK;
1172
}
1173
1174
bool D3D11DXVA2Manager::CanCreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc,
1175
const float aFramerate) const {
1176
MOZ_ASSERT(NS_IsMainThread());
1177
if (IsUnsupportedResolution(aDesc.SampleWidth, aDesc.SampleHeight,
1178
aFramerate)) {
1179
return false;
1180
}
1181
RefPtr<ID3D11VideoDecoder> decoder = CreateDecoder(aDesc);
1182
return decoder.get() != nullptr;
1183
}
1184
1185
already_AddRefed<ID3D11VideoDecoder> D3D11DXVA2Manager::CreateDecoder(
1186
const D3D11_VIDEO_DECODER_DESC& aDesc) const {
1187
MOZ_ASSERT(NS_IsMainThread());
1188
gfx::D3D11VideoCrashGuard crashGuard;
1189
if (crashGuard.Crashed()) {
1190
NS_WARNING("DXVA2D3D9 crash detected");
1191
return nullptr;
1192
}
1193
1194
RefPtr<ID3D11VideoDevice> videoDevice;
1195
HRESULT hr = mDevice->QueryInterface(
1196
static_cast<ID3D11VideoDevice**>(getter_AddRefs(videoDevice)));
1197
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
1198
1199
UINT configCount = 0;
1200
hr = videoDevice->GetVideoDecoderConfigCount(&aDesc, &configCount);
1201
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
1202
1203
for (UINT i = 0; i < configCount; i++) {
1204
D3D11_VIDEO_DECODER_CONFIG config;
1205
hr = videoDevice->GetVideoDecoderConfig(&aDesc, i, &config);
1206
if (SUCCEEDED(hr)) {
1207
RefPtr<ID3D11VideoDecoder> decoder;
1208
hr = videoDevice->CreateVideoDecoder(&aDesc, &config,
1209
decoder.StartAssignment());
1210
return decoder.forget();
1211
}
1212
}
1213
return nullptr;
1214
}
1215
1216
/* static */
1217
DXVA2Manager* DXVA2Manager::CreateD3D11DXVA(
1218
layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason,
1219
ID3D11Device* aDevice) {
1220
// DXVA processing takes up a lot of GPU resources, so limit the number of
1221
// videos we use DXVA with at any one time.
1222
uint32_t dxvaLimit = StaticPrefs::PDMWMFMaxDXVAVideos();
1223
1224
if (sDXVAVideosCount == dxvaLimit) {
1225
aFailureReason.AssignLiteral("Too many DXVA videos playing");
1226
return nullptr;
1227
}
1228
1229
nsAutoPtr<D3D11DXVA2Manager> manager(new D3D11DXVA2Manager());
1230
HRESULT hr = manager->Init(aKnowsCompositor, aFailureReason, aDevice);
1231
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
1232
1233
return manager.forget();
1234
}
1235
1236
DXVA2Manager::DXVA2Manager() : mLock("DXVA2Manager") {
1237
if (NS_IsMainThread()) {
1238
++sDXVAVideosCount;
1239
}
1240
}
1241
1242
DXVA2Manager::~DXVA2Manager() {
1243
if (NS_IsMainThread()) {
1244
--sDXVAVideosCount;
1245
}
1246
}
1247
1248
bool DXVA2Manager::IsUnsupportedResolution(const uint32_t& aWidth,
1249
const uint32_t& aHeight,
1250
const float& aFramerate) const {
1251
// AMD cards with UVD3 or earlier perform poorly trying to decode 1080p60 in
1252
// hardware, so use software instead. Pick 45 as an arbitrary upper bound for
1253
// the framerate we can handle.
1254
return !StaticPrefs::PDMWMFAMDHighResEnabled() && mIsAMDPreUVD4 &&
1255
(aWidth >= 1920 || aHeight >= 1088) && aFramerate > 45;
1256
}
1257
1258
/* static */
1259
bool DXVA2Manager::IsNV12Supported(uint32_t aVendorID, uint32_t aDeviceID,
1260
const nsAString& aDriverVersionString) {
1261
if (aVendorID == 0x1022 || aVendorID == 0x1002) {
1262
// AMD
1263
// Block old cards regardless of driver version.
1264
for (const auto& model : sAMDPreUVD4) {
1265
if (aDeviceID == model) {
1266
return false;
1267
}
1268
}
1269
// AMD driver earlier than 21.19.411.0 have bugs in their handling of NV12
1270
// surfaces.
1271
uint64_t driverVersion;
1272
if (!widget::ParseDriverVersion(aDriverVersionString, &driverVersion) ||
1273
driverVersion < widget::V(21, 19, 411, 0)) {
1274
return false;
1275
}
1276
} else if (aVendorID == 0x10DE) {
1277
// NVidia
1278
for (const auto& model : sNVIDIABrokenNV12) {
1279
if (aDeviceID == model) {
1280
return false;
1281
}
1282
}
1283
}
1284
return true;
1285
}
1286
1287
} // namespace mozilla