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_media.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::media_wmf_skip_blacklist()) {
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::media_wmf_dxva_max_videos();
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,
597
gfx::ColorRange aColorRange, uint32_t aWidth,
598
uint32_t aHeight) override;
599
600
bool IsD3D11() override { return true; }
601
602
bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
603
604
private:
605
HRESULT CreateFormatConverter();
606
607
HRESULT CreateOutputSample(RefPtr<IMFSample>& aSample,
608
ID3D11Texture2D* aTexture);
609
610
bool CanCreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc,
611
const float aFramerate) const;
612
613
already_AddRefed<ID3D11VideoDecoder> CreateDecoder(
614
const D3D11_VIDEO_DECODER_DESC& aDesc) const;
615
616
RefPtr<ID3D11Device> mDevice;
617
RefPtr<ID3D11DeviceContext> mContext;
618
RefPtr<IMFDXGIDeviceManager> mDXGIDeviceManager;
619
RefPtr<MFTDecoder> mTransform;
620
RefPtr<D3D11RecycleAllocator> mTextureClientAllocator;
621
RefPtr<ID3D11VideoDecoder> mDecoder;
622
RefPtr<layers::SyncObjectClient> mSyncObject;
623
GUID mDecoderGUID;
624
uint32_t mWidth = 0;
625
uint32_t mHeight = 0;
626
UINT mDeviceManagerToken = 0;
627
RefPtr<IMFMediaType> mInputType;
628
GUID mInputSubType;
629
gfx::YUVColorSpace mYUVColorSpace = gfx::YUVColorSpace::UNKNOWN;
630
gfx::ColorRange mColorRange = gfx::ColorRange::LIMITED;
631
};
632
633
bool D3D11DXVA2Manager::SupportsConfig(IMFMediaType* aType, float aFramerate) {
634
MOZ_ASSERT(NS_IsMainThread());
635
D3D11_VIDEO_DECODER_DESC desc;
636
desc.Guid = mDecoderGUID;
637
desc.OutputFormat = DXGI_FORMAT_NV12;
638
HRESULT hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &desc.SampleWidth,
639
&desc.SampleHeight);
640
NS_ENSURE_TRUE(SUCCEEDED(hr), false);
641
NS_ENSURE_TRUE(desc.SampleWidth <= MAX_VIDEO_WIDTH, false);
642
NS_ENSURE_TRUE(desc.SampleHeight <= MAX_VIDEO_HEIGHT, false);
643
644
return CanCreateDecoder(desc, aFramerate);
645
}
646
647
D3D11DXVA2Manager::~D3D11DXVA2Manager() {}
648
649
IUnknown* D3D11DXVA2Manager::GetDXVADeviceManager() {
650
MutexAutoLock lock(mLock);
651
return mDXGIDeviceManager;
652
}
653
HRESULT
654
D3D11DXVA2Manager::Init(layers::KnowsCompositor* aKnowsCompositor,
655
nsACString& aFailureReason, ID3D11Device* aDevice) {
656
if (!NS_IsMainThread()) {
657
// DXVA Managers used for full video have to be initialized on the main
658
// thread. Managers initialized off the main thread have to pass a device
659
// and can only be used for color conversion.
660
MOZ_ASSERT(aDevice);
661
return InitInternal(aKnowsCompositor, aFailureReason, aDevice);
662
}
663
664
HRESULT hr;
665
ScopedGfxFeatureReporter reporter("DXVA2D3D11");
666
667
gfx::D3D11VideoCrashGuard crashGuard;
668
if (crashGuard.Crashed()) {
669
NS_WARNING("DXVA2D3D11 crash detected");
670
aFailureReason.AssignLiteral("DXVA2D3D11 crashes detected in the past");
671
return E_FAIL;
672
}
673
674
hr = InitInternal(aKnowsCompositor, aFailureReason, aDevice);
675
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
676
677
if (layers::ImageBridgeChild::GetSingleton() || !aKnowsCompositor) {
678
// There's no proper KnowsCompositor for ImageBridge currently (and it
679
// implements the interface), so just use that if it's available.
680
mTextureClientAllocator = new D3D11RecycleAllocator(
681
layers::ImageBridgeChild::GetSingleton().get(), mDevice,
682
gfx::SurfaceFormat::NV12);
683
684
if (ImageBridgeChild::GetSingleton() &&
685
StaticPrefs::media_wmf_use_sync_texture_AtStartup() &&
686
mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) {
687
// We use a syncobject to avoid the cost of the mutex lock when
688
// compositing, and because it allows color conversion ocurring directly
689
// from this texture DXVA does not seem to accept IDXGIKeyedMutex textures
690
// as input.
691
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
692
layers::ImageBridgeChild::GetSingleton()
693
->GetTextureFactoryIdentifier()
694
.mSyncHandle,
695
mDevice);
696
}
697
} else {
698
mTextureClientAllocator = new D3D11RecycleAllocator(
699
aKnowsCompositor, mDevice, gfx::SurfaceFormat::NV12);
700
if (StaticPrefs::media_wmf_use_sync_texture_AtStartup()) {
701
// We use a syncobject to avoid the cost of the mutex lock when
702
// compositing, and because it allows color conversion ocurring directly
703
// from this texture DXVA does not seem to accept IDXGIKeyedMutex textures
704
// as input.
705
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
706
aKnowsCompositor->GetTextureFactoryIdentifier().mSyncHandle, mDevice);
707
}
708
}
709
mTextureClientAllocator->SetMaxPoolSize(5);
710
711
Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
712
uint32_t(media::MediaDecoderBackend::WMFDXVA2D3D11));
713
714
reporter.SetSuccessful();
715
716
return S_OK;
717
}
718
719
HRESULT
720
D3D11DXVA2Manager::InitInternal(layers::KnowsCompositor* aKnowsCompositor,
721
nsACString& aFailureReason,
722
ID3D11Device* aDevice) {
723
HRESULT hr;
724
725
mDevice = aDevice;
726
727
if (!mDevice) {
728
mDevice = gfx::DeviceManagerDx::Get()->CreateDecoderDevice();
729
if (!mDevice) {
730
aFailureReason.AssignLiteral("Failed to create D3D11 device for decoder");
731
return E_FAIL;
732
}
733
}
734
735
RefPtr<ID3D10Multithread> mt;
736
hr = mDevice->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt));
737
NS_ENSURE_TRUE(SUCCEEDED(hr) && mt, hr);
738
mt->SetMultithreadProtected(TRUE);
739
740
mDevice->GetImmediateContext(getter_AddRefs(mContext));
741
742
hr = wmf::MFCreateDXGIDeviceManager(&mDeviceManagerToken,
743
getter_AddRefs(mDXGIDeviceManager));
744
if (!SUCCEEDED(hr)) {
745
aFailureReason =
746
nsPrintfCString("MFCreateDXGIDeviceManager failed with code %X", hr);
747
return hr;
748
}
749
750
hr = mDXGIDeviceManager->ResetDevice(mDevice, mDeviceManagerToken);
751
if (!SUCCEEDED(hr)) {
752
aFailureReason = nsPrintfCString(
753
"IMFDXGIDeviceManager::ResetDevice failed with code %X", hr);
754
return hr;
755
}
756
757
// The IMFTransform interface used by MFTDecoder is documented to require to
758
// run on an MTA thread.
760
// The main thread (where this function is called) is STA, not MTA.
761
RefPtr<MFTDecoder> mft;
762
mozilla::mscom::EnsureMTA([&]() -> void {
763
mft = new MFTDecoder();
764
hr = mft->Create(CLSID_VideoProcessorMFT);
765
766
if (!SUCCEEDED(hr)) {
767
aFailureReason = nsPrintfCString(
768
"MFTDecoder::Create(CLSID_VideoProcessorMFT) failed with code %X",
769
hr);
770
return;
771
}
772
773
hr = mft->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER,
774
ULONG_PTR(mDXGIDeviceManager.get()));
775
if (!SUCCEEDED(hr)) {
776
aFailureReason = nsPrintfCString(
777
"MFTDecoder::SendMFTMessage(MFT_MESSAGE_"
778
"SET_D3D_MANAGER) failed with code %X",
779
hr);
780
return;
781
}
782
});
783
784
if (!SUCCEEDED(hr)) {
785
return hr;
786
}
787
mTransform = mft;
788
789
RefPtr<ID3D11VideoDevice> videoDevice;
790
hr = mDevice->QueryInterface(
791
static_cast<ID3D11VideoDevice**>(getter_AddRefs(videoDevice)));
792
if (!SUCCEEDED(hr)) {
793
aFailureReason =
794
nsPrintfCString("QI to ID3D11VideoDevice failed with code %X", hr);
795
return hr;
796
}
797
798
bool found = false;
799
UINT profileCount = videoDevice->GetVideoDecoderProfileCount();
800
for (UINT i = 0; i < profileCount; i++) {
801
GUID id;
802
hr = videoDevice->GetVideoDecoderProfile(i, &id);
803
if (SUCCEEDED(hr) &&
804
(id == DXVA2_ModeH264_E || id == DXVA2_Intel_ModeH264_E)) {
805
mDecoderGUID = id;
806
found = true;
807
break;
808
}
809
}
810
if (!found) {
811
aFailureReason.AssignLiteral("Failed to find an appropriate decoder GUID");
812
return E_FAIL;
813
}
814
815
BOOL nv12Support = false;
816
hr = videoDevice->CheckVideoDecoderFormat(&mDecoderGUID, DXGI_FORMAT_NV12,
817
&nv12Support);
818
if (!SUCCEEDED(hr)) {
819
aFailureReason =
820
nsPrintfCString("CheckVideoDecoderFormat failed with code %X", hr);
821
return hr;
822
}
823
if (!nv12Support) {
824
aFailureReason.AssignLiteral("Decoder doesn't support NV12 surfaces");
825
return E_FAIL;
826
}
827
828
RefPtr<IDXGIDevice> dxgiDevice;
829
hr = mDevice->QueryInterface(
830
static_cast<IDXGIDevice**>(getter_AddRefs(dxgiDevice)));
831
if (!SUCCEEDED(hr)) {
832
aFailureReason =
833
nsPrintfCString("QI to IDXGIDevice failed with code %X", hr);
834
return hr;
835
}
836
837
RefPtr<IDXGIAdapter> adapter;
838
hr = dxgiDevice->GetAdapter(adapter.StartAssignment());
839
if (!SUCCEEDED(hr)) {
840
aFailureReason =
841
nsPrintfCString("IDXGIDevice::GetAdapter failed with code %X", hr);
842
return hr;
843
}
844
845
DXGI_ADAPTER_DESC adapterDesc;
846
hr = adapter->GetDesc(&adapterDesc);
847
if (!SUCCEEDED(hr)) {
848
aFailureReason =
849
nsPrintfCString("IDXGIAdapter::GetDesc failed with code %X", hr);
850
return hr;
851
}
852
853
if ((adapterDesc.VendorId == 0x1022 || adapterDesc.VendorId == 0x1002) &&
854
!StaticPrefs::media_wmf_skip_blacklist()) {
855
for (const auto& model : sAMDPreUVD4) {
856
if (adapterDesc.DeviceId == model) {
857
mIsAMDPreUVD4 = true;
858
break;
859
}
860
}
861
}
862
863
return S_OK;
864
}
865
866
HRESULT
867
D3D11DXVA2Manager::CreateOutputSample(RefPtr<IMFSample>& aSample,
868
ID3D11Texture2D* aTexture) {
869
RefPtr<IMFSample> sample;
870
HRESULT hr = wmf::MFCreateSample(getter_AddRefs(sample));
871
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
872
873
RefPtr<IMFMediaBuffer> buffer;
874
hr = wmf::MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), aTexture, 0,
875
FALSE, getter_AddRefs(buffer));
876
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
877
878
hr = sample->AddBuffer(buffer);
879
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
880
881
aSample = sample;
882
return S_OK;
883
}
884
885
HRESULT
886
D3D11DXVA2Manager::CopyToImage(IMFSample* aVideoSample,
887
const gfx::IntRect& aRegion, Image** aOutImage) {
888
NS_ENSURE_TRUE(aVideoSample, E_POINTER);
889
NS_ENSURE_TRUE(aOutImage, E_POINTER);
890
MOZ_ASSERT(mTextureClientAllocator);
891
892
RefPtr<D3D11ShareHandleImage> image = new D3D11ShareHandleImage(
893
gfx::IntSize(mWidth, mHeight), aRegion, mYUVColorSpace, mColorRange);
894
895
// Retrieve the DXGI_FORMAT for the current video sample.
896
RefPtr<IMFMediaBuffer> buffer;
897
HRESULT hr = aVideoSample->GetBufferByIndex(0, getter_AddRefs(buffer));
898
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
899
900
RefPtr<IMFDXGIBuffer> dxgiBuf;
901
hr = buffer->QueryInterface((IMFDXGIBuffer**)getter_AddRefs(dxgiBuf));
902
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
903
904
RefPtr<ID3D11Texture2D> tex;
905
hr = dxgiBuf->GetResource(__uuidof(ID3D11Texture2D), getter_AddRefs(tex));
906
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
907
908
D3D11_TEXTURE2D_DESC inDesc;
909
tex->GetDesc(&inDesc);
910
911
bool ok = image->AllocateTexture(mTextureClientAllocator, mDevice);
912
NS_ENSURE_TRUE(ok, E_FAIL);
913
914
RefPtr<TextureClient> client =
915
image->GetTextureClient(ImageBridgeChild::GetSingleton().get());
916
NS_ENSURE_TRUE(client, E_FAIL);
917
918
RefPtr<ID3D11Texture2D> texture = image->GetTexture();
919
D3D11_TEXTURE2D_DESC outDesc;
920
texture->GetDesc(&outDesc);
921
922
RefPtr<IDXGIKeyedMutex> mutex;
923
texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
924
925
{
926
AutoTextureLock(mutex, hr, 2000);
927
if (mutex && (FAILED(hr) || hr == WAIT_TIMEOUT || hr == WAIT_ABANDONED)) {
928
return hr;
929
}
930
931
if (!mutex && mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) {
932
NS_ENSURE_TRUE(mSyncObject, E_FAIL);
933
}
934
935
// The D3D11TextureClientAllocator may return a different texture format
936
// than preferred. In which case the destination texture will be BGRA32.
937
if (outDesc.Format == inDesc.Format) {
938
// Our video frame is stored in a non-sharable ID3D11Texture2D. We need
939
// to create a copy of that frame as a sharable resource, save its share
940
// handle, and put that handle into the rendering pipeline.
941
UINT width = std::min(inDesc.Width, outDesc.Width);
942
UINT height = std::min(inDesc.Height, outDesc.Height);
943
D3D11_BOX srcBox = {0, 0, 0, width, height, 1};
944
945
UINT index;
946
dxgiBuf->GetSubresourceIndex(&index);
947
mContext->CopySubresourceRegion(texture, 0, 0, 0, 0, tex, index, &srcBox);
948
} else {
949
// Use MFT to do color conversion.
950
hr = E_FAIL;
951
mozilla::mscom::EnsureMTA(
952
[&]() -> void { hr = mTransform->Input(aVideoSample); });
953
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
954
955
RefPtr<IMFSample> sample;
956
hr = CreateOutputSample(sample, texture);
957
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
958
959
hr = E_FAIL;
960
mozilla::mscom::EnsureMTA(
961
[&]() -> void { hr = mTransform->Output(&sample); });
962
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
963
}
964
}
965
966
if (!mutex && mDevice != DeviceManagerDx::Get()->GetCompositorDevice() &&
967
mSyncObject) {
968
// It appears some race-condition may allow us to arrive here even when
969
// mSyncObject is null. It's better to avoid that crash.
970
client->SyncWithObject(mSyncObject);
971
if (!mSyncObject->Synchronize(true)) {
972
return DXGI_ERROR_DEVICE_RESET;
973
}
974
}
975
976
image.forget(aOutImage);
977
978
return S_OK;
979
}
980
981
HRESULT
982
D3D11DXVA2Manager::CopyToBGRATexture(ID3D11Texture2D* aInTexture,
983
ID3D11Texture2D** aOutTexture) {
984
NS_ENSURE_TRUE(aInTexture, E_POINTER);
985
NS_ENSURE_TRUE(aOutTexture, E_POINTER);
986
987
HRESULT hr;
988
RefPtr<ID3D11Texture2D> texture, inTexture;
989
990
inTexture = aInTexture;
991
992
CD3D11_TEXTURE2D_DESC desc;
993
aInTexture->GetDesc(&desc);
994
995
if (!mInputType || desc.Width != mWidth || desc.Height != mHeight) {
996
RefPtr<IMFMediaType> inputType;
997
hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
998
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
999
1000
hr = inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
1001
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1002
1003
const GUID subType = [&]() {
1004
switch (desc.Format) {
1005
case DXGI_FORMAT_NV12:
1006
return MFVideoFormat_NV12;
1007
case DXGI_FORMAT_P010:
1008
return MFVideoFormat_P010;
1009
case DXGI_FORMAT_P016:
1010
return MFVideoFormat_P016;
1011
default:
1012
MOZ_ASSERT_UNREACHABLE("Unexpected texture type");
1013
return MFVideoFormat_NV12;
1014
}
1015
}();
1016
1017
hr = inputType->SetGUID(MF_MT_SUBTYPE, subType);
1018
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1019
1020
hr = inputType->SetUINT32(MF_MT_INTERLACE_MODE,
1021
MFVideoInterlace_Progressive);
1022
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1023
1024
hr = inputType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
1025
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1026
1027
hr = ConfigureForSize(inputType, mYUVColorSpace, mColorRange, desc.Width,
1028
desc.Height);
1029
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1030
}
1031
1032
RefPtr<IDXGIKeyedMutex> mutex;
1033
inTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
1034
// The rest of this function will not work if inTexture implements
1035
// IDXGIKeyedMutex! In that case case we would have to copy to a
1036
// non-mutex using texture.
1037
1038
if (mutex) {
1039
RefPtr<ID3D11Texture2D> newTexture;
1040
1041
desc.MiscFlags = 0;
1042
hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(newTexture));
1043
NS_ENSURE_TRUE(SUCCEEDED(hr) && newTexture, E_FAIL);
1044
1045
hr = mutex->AcquireSync(0, 2000);
1046
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1047
1048
mContext->CopyResource(newTexture, inTexture);
1049
1050
mutex->ReleaseSync(0);
1051
inTexture = newTexture;
1052
}
1053
1054
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
1055
1056
hr = mDevice->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
1057
NS_ENSURE_TRUE(SUCCEEDED(hr) && texture, E_FAIL);
1058
1059
RefPtr<IMFSample> inputSample;
1060
wmf::MFCreateSample(getter_AddRefs(inputSample));
1061
1062
// If these aren't set the decoder fails.
1063
inputSample->SetSampleTime(10);
1064
inputSample->SetSampleDuration(10000);
1065
1066
RefPtr<IMFMediaBuffer> inputBuffer;
1067
hr = wmf::MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), inTexture, 0,
1068
FALSE, getter_AddRefs(inputBuffer));
1069
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1070
1071
inputSample->AddBuffer(inputBuffer);
1072
1073
hr = E_FAIL;
1074
mozilla::mscom::EnsureMTA(
1075
[&]() -> void { hr = mTransform->Input(inputSample); });
1076
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1077
1078
RefPtr<IMFSample> outputSample;
1079
hr = CreateOutputSample(outputSample, texture);
1080
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1081
1082
hr = E_FAIL;
1083
mozilla::mscom::EnsureMTA(
1084
[&]() -> void { hr = mTransform->Output(&outputSample); });
1085
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1086
1087
texture.forget(aOutTexture);
1088
1089
return S_OK;
1090
}
1091
1092
HRESULT
1093
D3D11DXVA2Manager::ConfigureForSize(IMFMediaType* aInputType,
1094
gfx::YUVColorSpace aColorSpace,
1095
gfx::ColorRange aColorRange,
1096
uint32_t aWidth, uint32_t aHeight) {
1097
GUID subType = {0};
1098
HRESULT hr = aInputType->GetGUID(MF_MT_SUBTYPE, &subType);
1099
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1100
1101
if (subType == mInputSubType && aWidth == mWidth && aHeight == mHeight &&
1102
mYUVColorSpace == aColorSpace && mColorRange == aColorRange) {
1103
// If the media type hasn't changed, don't reconfigure.
1104
return S_OK;
1105
}
1106
1107
// Create a copy of our input type.
1108
RefPtr<IMFMediaType> inputType;
1109
hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
1110
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1111
hr = aInputType->CopyAllItems(inputType);
1112
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1113
1114
hr = MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE, aWidth, aHeight);
1115
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1116
1117
RefPtr<IMFAttributes> attr;
1118
mozilla::mscom::EnsureMTA(
1119
[&]() -> void { attr = mTransform->GetAttributes(); });
1120
NS_ENSURE_TRUE(attr != nullptr, E_FAIL);
1121
1122
hr = attr->SetUINT32(MF_XVP_PLAYBACK_MODE, TRUE);
1123
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1124
1125
hr = attr->SetUINT32(MF_LOW_LATENCY, FALSE);
1126
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1127
1128
RefPtr<IMFMediaType> outputType;
1129
hr = wmf::MFCreateMediaType(getter_AddRefs(outputType));
1130
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1131
1132
hr = outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
1133
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1134
1135
hr = outputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
1136
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1137
1138
hr = E_FAIL;
1139
mozilla::mscom::EnsureMTA([&]() -> void {
1140
hr = mTransform->SetMediaTypes(
1141
inputType, outputType, [aWidth, aHeight](IMFMediaType* aOutput) {
1142
HRESULT hr = aOutput->SetUINT32(MF_MT_INTERLACE_MODE,
1143
MFVideoInterlace_Progressive);
1144
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1145
hr = aOutput->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
1146
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1147
hr = MFSetAttributeSize(aOutput, MF_MT_FRAME_SIZE, aWidth, aHeight);
1148
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1149
1150
return S_OK;
1151
});
1152
});
1153
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1154
1155
mWidth = aWidth;
1156
mHeight = aHeight;
1157
mInputType = inputType;
1158
mInputSubType = subType;
1159
mYUVColorSpace = aColorSpace;
1160
mColorRange = aColorRange;
1161
if (mTextureClientAllocator) {
1162
gfx::SurfaceFormat format = [&]() {
1163
if (subType == MFVideoFormat_NV12) {
1164
return gfx::SurfaceFormat::NV12;
1165
} else if (subType == MFVideoFormat_P010) {
1166
return gfx::SurfaceFormat::P010;
1167
} else if (subType == MFVideoFormat_P016) {
1168
return gfx::SurfaceFormat::P016;
1169
} else {
1170
MOZ_ASSERT_UNREACHABLE("Unexpected texture type");
1171
return gfx::SurfaceFormat::NV12;
1172
}
1173
}();
1174
mTextureClientAllocator->SetPreferredSurfaceFormat(format);
1175
}
1176
return S_OK;
1177
}
1178
1179
bool D3D11DXVA2Manager::CanCreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc,
1180
const float aFramerate) const {
1181
MOZ_ASSERT(NS_IsMainThread());
1182
if (IsUnsupportedResolution(aDesc.SampleWidth, aDesc.SampleHeight,
1183
aFramerate)) {
1184
return false;
1185
}
1186
RefPtr<ID3D11VideoDecoder> decoder = CreateDecoder(aDesc);
1187
return decoder.get() != nullptr;
1188
}
1189
1190
already_AddRefed<ID3D11VideoDecoder> D3D11DXVA2Manager::CreateDecoder(
1191
const D3D11_VIDEO_DECODER_DESC& aDesc) const {
1192
MOZ_ASSERT(NS_IsMainThread());
1193
gfx::D3D11VideoCrashGuard crashGuard;
1194
if (crashGuard.Crashed()) {
1195
NS_WARNING("DXVA2D3D9 crash detected");
1196
return nullptr;
1197
}
1198
1199
RefPtr<ID3D11VideoDevice> videoDevice;
1200
HRESULT hr = mDevice->QueryInterface(
1201
static_cast<ID3D11VideoDevice**>(getter_AddRefs(videoDevice)));
1202
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
1203
1204
UINT configCount = 0;
1205
hr = videoDevice->GetVideoDecoderConfigCount(&aDesc, &configCount);
1206
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
1207
1208
for (UINT i = 0; i < configCount; i++) {
1209
D3D11_VIDEO_DECODER_CONFIG config;
1210
hr = videoDevice->GetVideoDecoderConfig(&aDesc, i, &config);
1211
if (SUCCEEDED(hr)) {
1212
RefPtr<ID3D11VideoDecoder> decoder;
1213
hr = videoDevice->CreateVideoDecoder(&aDesc, &config,
1214
decoder.StartAssignment());
1215
return decoder.forget();
1216
}
1217
}
1218
return nullptr;
1219
}
1220
1221
/* static */
1222
DXVA2Manager* DXVA2Manager::CreateD3D11DXVA(
1223
layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason,
1224
ID3D11Device* aDevice) {
1225
// DXVA processing takes up a lot of GPU resources, so limit the number of
1226
// videos we use DXVA with at any one time.
1227
uint32_t dxvaLimit = StaticPrefs::media_wmf_dxva_max_videos();
1228
1229
if (sDXVAVideosCount == dxvaLimit) {
1230
aFailureReason.AssignLiteral("Too many DXVA videos playing");
1231
return nullptr;
1232
}
1233
1234
nsAutoPtr<D3D11DXVA2Manager> manager(new D3D11DXVA2Manager());
1235
HRESULT hr = manager->Init(aKnowsCompositor, aFailureReason, aDevice);
1236
NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
1237
1238
return manager.forget();
1239
}
1240
1241
DXVA2Manager::DXVA2Manager() : mLock("DXVA2Manager") {
1242
if (NS_IsMainThread()) {
1243
++sDXVAVideosCount;
1244
}
1245
}
1246
1247
DXVA2Manager::~DXVA2Manager() {
1248
if (NS_IsMainThread()) {
1249
--sDXVAVideosCount;
1250
}
1251
}
1252
1253
bool DXVA2Manager::IsUnsupportedResolution(const uint32_t& aWidth,
1254
const uint32_t& aHeight,
1255
const float& aFramerate) const {
1256
// AMD cards with UVD3 or earlier perform poorly trying to decode 1080p60 in
1257
// hardware, so use software instead. Pick 45 as an arbitrary upper bound for
1258
// the framerate we can handle.
1259
return !StaticPrefs::media_wmf_amd_highres_enabled() && mIsAMDPreUVD4 &&
1260
(aWidth >= 1920 || aHeight >= 1088) && aFramerate > 45;
1261
}
1262
1263
/* static */
1264
bool DXVA2Manager::IsNV12Supported(uint32_t aVendorID, uint32_t aDeviceID,
1265
const nsAString& aDriverVersionString) {
1266
if (aVendorID == 0x1022 || aVendorID == 0x1002) {
1267
// AMD
1268
// Block old cards regardless of driver version.
1269
for (const auto& model : sAMDPreUVD4) {
1270
if (aDeviceID == model) {
1271
return false;
1272
}
1273
}
1274
// AMD driver earlier than 21.19.411.0 have bugs in their handling of NV12
1275
// surfaces.
1276
uint64_t driverVersion;
1277
if (!widget::ParseDriverVersion(aDriverVersionString, &driverVersion) ||
1278
driverVersion < widget::V(21, 19, 411, 0)) {
1279
return false;
1280
}
1281
} else if (aVendorID == 0x10DE) {
1282
// NVidia
1283
for (const auto& model : sNVIDIABrokenNV12) {
1284
if (aDeviceID == model) {
1285
return false;
1286
}
1287
}
1288
}
1289
return true;
1290
}
1291
1292
} // namespace mozilla