1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "D3D11Checks.h"
7 #include "DXVA2Manager.h"
8 #include "gfxConfig.h"
9 #include "GfxDriverInfo.h"
10 #include "gfxPrefs.h"
11 #include "gfxWindowsPlatform.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/gfx/gfxVars.h"
14 #include "mozilla/gfx/Logging.h"
15 #include "mozilla/layers/TextureD3D11.h"
16 #include "nsIGfxInfo.h"
17 #include <dxgi.h>
18 #include <dxgi1_2.h>
19 #include <d3d10_1.h>
20 #include <d3d11.h>
21
22 namespace mozilla {
23 namespace gfx {
24
25 using namespace mozilla::widget;
26 using mozilla::layers::AutoTextureLock;
27
DoesRenderTargetViewNeedRecreating(ID3D11Device * aDevice)28 /* static */ bool D3D11Checks::DoesRenderTargetViewNeedRecreating(
29 ID3D11Device* aDevice) {
30 bool result = false;
31 // CreateTexture2D is known to crash on lower feature levels, see bugs
32 // 1170211 and 1089413.
33 if (aDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
34 return true;
35 }
36
37 RefPtr<ID3D11DeviceContext> deviceContext;
38 aDevice->GetImmediateContext(getter_AddRefs(deviceContext));
39 int backbufferWidth = 32;
40 int backbufferHeight = 32;
41 RefPtr<ID3D11Texture2D> offscreenTexture;
42 RefPtr<IDXGIKeyedMutex> keyedMutex;
43
44 D3D11_TEXTURE2D_DESC offscreenTextureDesc = {0};
45 offscreenTextureDesc.Width = backbufferWidth;
46 offscreenTextureDesc.Height = backbufferHeight;
47 offscreenTextureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
48 offscreenTextureDesc.MipLevels = 0;
49 offscreenTextureDesc.ArraySize = 1;
50 offscreenTextureDesc.SampleDesc.Count = 1;
51 offscreenTextureDesc.SampleDesc.Quality = 0;
52 offscreenTextureDesc.Usage = D3D11_USAGE_DEFAULT;
53 offscreenTextureDesc.BindFlags =
54 D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
55 offscreenTextureDesc.CPUAccessFlags = 0;
56 offscreenTextureDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
57
58 HRESULT hr = aDevice->CreateTexture2D(&offscreenTextureDesc, NULL,
59 getter_AddRefs(offscreenTexture));
60 if (FAILED(hr)) {
61 gfxCriticalNote << "DoesRecreatingCreateTexture2DFail";
62 return false;
63 }
64
65 hr = offscreenTexture->QueryInterface(__uuidof(IDXGIKeyedMutex),
66 (void**)getter_AddRefs(keyedMutex));
67 if (FAILED(hr)) {
68 gfxCriticalNote << "DoesRecreatingKeyedMutexFailed";
69 return false;
70 }
71 D3D11_RENDER_TARGET_VIEW_DESC offscreenRTVDesc;
72 offscreenRTVDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
73 offscreenRTVDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
74 offscreenRTVDesc.Texture2D.MipSlice = 0;
75
76 RefPtr<ID3D11RenderTargetView> offscreenRTView;
77 hr = aDevice->CreateRenderTargetView(offscreenTexture, &offscreenRTVDesc,
78 getter_AddRefs(offscreenRTView));
79 if (FAILED(hr)) {
80 gfxCriticalNote << "DoesRecreatingCreateRenderTargetViewFailed";
81 return false;
82 }
83
84 {
85 // Acquire and clear
86 HRESULT hr;
87 AutoTextureLock lock(keyedMutex, hr, INFINITE);
88 FLOAT color1[4] = {1, 1, 0.5, 1};
89 deviceContext->ClearRenderTargetView(offscreenRTView, color1);
90 }
91
92 {
93 HRESULT hr;
94 AutoTextureLock lock(keyedMutex, hr, INFINITE);
95 FLOAT color2[4] = {1, 1, 0, 1};
96
97 deviceContext->ClearRenderTargetView(offscreenRTView, color2);
98 D3D11_TEXTURE2D_DESC desc;
99
100 offscreenTexture->GetDesc(&desc);
101 desc.Usage = D3D11_USAGE_STAGING;
102 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
103 desc.MiscFlags = 0;
104 desc.BindFlags = 0;
105 RefPtr<ID3D11Texture2D> cpuTexture;
106 hr = aDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(cpuTexture));
107 if (FAILED(hr)) {
108 gfxCriticalNote << "DoesRecreatingCreateCPUTextureFailed";
109 return false;
110 }
111
112 deviceContext->CopyResource(cpuTexture, offscreenTexture);
113
114 D3D11_MAPPED_SUBRESOURCE mapped;
115 hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped);
116 if (FAILED(hr)) {
117 gfxCriticalNote << "DoesRecreatingMapFailed " << hexa(hr);
118 return false;
119 }
120 int resultColor = *(int*)mapped.pData;
121 deviceContext->Unmap(cpuTexture, 0);
122 cpuTexture = nullptr;
123
124 // XXX on some drivers resultColor will not have changed to
125 // match the clear
126 if (resultColor != 0xffffff00) {
127 gfxCriticalNote << "RenderTargetViewNeedsRecreating";
128 result = true;
129 }
130 }
131 return result;
132 }
133
DoesDeviceWork()134 /* static */ bool D3D11Checks::DoesDeviceWork() {
135 static bool checked = false;
136 static bool result = false;
137
138 if (checked) return result;
139 checked = true;
140
141 if (gfxPrefs::Direct2DForceEnabled() ||
142 gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
143 result = true;
144 return true;
145 }
146
147 if (GetModuleHandleW(L"igd10umd32.dll")) {
148 const wchar_t* checkModules[] = {L"dlumd32.dll", L"dlumd11.dll",
149 L"dlumd10.dll"};
150 for (int i = 0; i < PR_ARRAY_SIZE(checkModules); i += 1) {
151 if (GetModuleHandleW(checkModules[i])) {
152 nsString displayLinkModuleVersionString;
153 gfxWindowsPlatform::GetDLLVersion(checkModules[i],
154 displayLinkModuleVersionString);
155 uint64_t displayLinkModuleVersion;
156 if (!ParseDriverVersion(displayLinkModuleVersionString,
157 &displayLinkModuleVersion)) {
158 gfxCriticalError()
159 << "DisplayLink: could not parse version " << checkModules[i];
160 return false;
161 }
162 if (displayLinkModuleVersion <= V(8, 6, 1, 36484)) {
163 NS_ConvertUTF16toUTF8 version(displayLinkModuleVersionString);
164 gfxCriticalError(CriticalLog::DefaultOptions(false))
165 << "DisplayLink: too old version " << version.get();
166 return false;
167 }
168 }
169 }
170 }
171 result = true;
172 return true;
173 }
174
TryCreateTexture2D(ID3D11Device * device,D3D11_TEXTURE2D_DESC * desc,D3D11_SUBRESOURCE_DATA * data,RefPtr<ID3D11Texture2D> & texture)175 static bool TryCreateTexture2D(ID3D11Device* device, D3D11_TEXTURE2D_DESC* desc,
176 D3D11_SUBRESOURCE_DATA* data,
177 RefPtr<ID3D11Texture2D>& texture) {
178 // Older Intel driver version (see bug 1221348 for version #s) crash when
179 // creating a texture with shared keyed mutex and data.
180 MOZ_SEH_TRY {
181 return !FAILED(
182 device->CreateTexture2D(desc, data, getter_AddRefs(texture)));
183 }
184 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
185 // For now we want to aggregrate all the crash signature to a known crash.
186 gfxDevCrash(LogReason::TextureCreation)
187 << "Crash creating texture. See bug 1221348.";
188 return false;
189 }
190 }
191
192 // See bug 1083071. On some drivers, Direct3D 11 CreateShaderResourceView fails
193 // with E_OUTOFMEMORY.
DoesTextureSharingWorkInternal(ID3D11Device * device,DXGI_FORMAT format,UINT bindflags)194 static bool DoesTextureSharingWorkInternal(ID3D11Device* device,
195 DXGI_FORMAT format, UINT bindflags) {
196 // CreateTexture2D is known to crash on lower feature levels, see bugs
197 // 1170211 and 1089413.
198 if (device->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0) {
199 return false;
200 }
201
202 if (gfxPrefs::Direct2DForceEnabled() ||
203 gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING)) {
204 return true;
205 }
206
207 if (GetModuleHandleW(L"atidxx32.dll")) {
208 nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
209 if (gfxInfo) {
210 nsString vendorID, vendorID2;
211 gfxInfo->GetAdapterVendorID(vendorID);
212 gfxInfo->GetAdapterVendorID2(vendorID2);
213 if (vendorID.EqualsLiteral("0x8086") && vendorID2.IsEmpty()) {
214 if (!gfxPrefs::LayersAMDSwitchableGfxEnabled()) {
215 return false;
216 }
217 gfxCriticalError(CriticalLog::DefaultOptions(false))
218 << "PossiblyBrokenSurfaceSharing_UnexpectedAMDGPU";
219 }
220 }
221 }
222
223 RefPtr<ID3D11Texture2D> texture;
224 D3D11_TEXTURE2D_DESC desc;
225 const int texture_size = 32;
226 desc.Width = texture_size;
227 desc.Height = texture_size;
228 desc.MipLevels = 1;
229 desc.ArraySize = 1;
230 desc.Format = format;
231 desc.SampleDesc.Count = 1;
232 desc.SampleDesc.Quality = 0;
233 desc.Usage = D3D11_USAGE_DEFAULT;
234 desc.CPUAccessFlags = 0;
235 desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
236 desc.BindFlags = bindflags;
237
238 uint32_t color[texture_size * texture_size];
239 for (size_t i = 0; i < sizeof(color) / sizeof(color[0]); i++) {
240 color[i] = 0xff00ffff;
241 }
242 // XXX If we pass the data directly at texture creation time we
243 // get a crash on Intel 8.5.10.[18xx-1994] drivers.
244 // We can work around this issue by doing UpdateSubresource.
245 if (!TryCreateTexture2D(device, &desc, nullptr, texture)) {
246 gfxCriticalNote << "DoesD3D11TextureSharingWork_TryCreateTextureFailure";
247 return false;
248 }
249
250 RefPtr<IDXGIKeyedMutex> sourceSharedMutex;
251 texture->QueryInterface(__uuidof(IDXGIKeyedMutex),
252 (void**)getter_AddRefs(sourceSharedMutex));
253 if (FAILED(sourceSharedMutex->AcquireSync(0, 30 * 1000))) {
254 gfxCriticalError() << "DoesD3D11TextureSharingWork_SourceMutexTimeout";
255 // only wait for 30 seconds
256 return false;
257 }
258
259 RefPtr<ID3D11DeviceContext> deviceContext;
260 device->GetImmediateContext(getter_AddRefs(deviceContext));
261
262 int stride = texture_size * 4;
263 deviceContext->UpdateSubresource(texture, 0, nullptr, color, stride,
264 stride * texture_size);
265
266 if (FAILED(sourceSharedMutex->ReleaseSync(0))) {
267 gfxCriticalError()
268 << "DoesD3D11TextureSharingWork_SourceReleaseSyncTimeout";
269 return false;
270 }
271
272 HANDLE shareHandle;
273 RefPtr<IDXGIResource> otherResource;
274 if (FAILED(texture->QueryInterface(__uuidof(IDXGIResource),
275 getter_AddRefs(otherResource)))) {
276 gfxCriticalError() << "DoesD3D11TextureSharingWork_GetResourceFailure";
277 return false;
278 }
279
280 if (FAILED(otherResource->GetSharedHandle(&shareHandle))) {
281 gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
282 return false;
283 }
284
285 RefPtr<ID3D11Resource> sharedResource;
286 RefPtr<ID3D11Texture2D> sharedTexture;
287 if (FAILED(device->OpenSharedResource(shareHandle, __uuidof(ID3D11Resource),
288 getter_AddRefs(sharedResource)))) {
289 gfxCriticalError(CriticalLog::DefaultOptions(false))
290 << "OpenSharedResource failed for format " << format;
291 return false;
292 }
293
294 if (FAILED(sharedResource->QueryInterface(__uuidof(ID3D11Texture2D),
295 getter_AddRefs(sharedTexture)))) {
296 gfxCriticalError() << "DoesD3D11TextureSharingWork_GetSharedTextureFailure";
297 return false;
298 }
299
300 // create a staging texture for readback
301 RefPtr<ID3D11Texture2D> cpuTexture;
302 desc.Usage = D3D11_USAGE_STAGING;
303 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
304 desc.MiscFlags = 0;
305 desc.BindFlags = 0;
306 if (FAILED(device->CreateTexture2D(&desc, nullptr,
307 getter_AddRefs(cpuTexture)))) {
308 gfxCriticalError() << "DoesD3D11TextureSharingWork_CreateTextureFailure";
309 return false;
310 }
311
312 RefPtr<IDXGIKeyedMutex> sharedMutex;
313 sharedResource->QueryInterface(__uuidof(IDXGIKeyedMutex),
314 (void**)getter_AddRefs(sharedMutex));
315 {
316 HRESULT hr;
317 AutoTextureLock lock(sharedMutex, hr, 30 * 1000);
318 if (FAILED(hr)) {
319 gfxCriticalError() << "DoesD3D11TextureSharingWork_AcquireSyncTimeout";
320 // only wait for 30 seconds
321 return false;
322 }
323
324 // Copy to the cpu texture so that we can readback
325 deviceContext->CopyResource(cpuTexture, sharedTexture);
326
327 // We only need to hold on to the mutex during the copy.
328 sharedMutex->ReleaseSync(0);
329 }
330
331 D3D11_MAPPED_SUBRESOURCE mapped;
332 uint32_t resultColor = 0;
333 if (SUCCEEDED(
334 deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
335 // read the texture
336 resultColor = *(uint32_t*)mapped.pData;
337 deviceContext->Unmap(cpuTexture, 0);
338 } else {
339 gfxCriticalError() << "DoesD3D11TextureSharingWork_MapFailed";
340 return false;
341 }
342
343 // check that the color we put in is the color we get out
344 if (resultColor != color[0]) {
345 // Shared surfaces seem to be broken on dual AMD & Intel HW when using the
346 // AMD GPU
347 gfxCriticalNote << "DoesD3D11TextureSharingWork_ColorMismatch";
348 return false;
349 }
350
351 RefPtr<ID3D11ShaderResourceView> sharedView;
352
353 // This if(FAILED()) is the one that actually fails on systems affected by bug
354 // 1083071.
355 if (FAILED(device->CreateShaderResourceView(sharedTexture, NULL,
356 getter_AddRefs(sharedView)))) {
357 gfxCriticalNote << "CreateShaderResourceView failed for format" << format;
358 return false;
359 }
360
361 return true;
362 }
363
DoesTextureSharingWork(ID3D11Device * device)364 /* static */ bool D3D11Checks::DoesTextureSharingWork(ID3D11Device* device) {
365 return DoesTextureSharingWorkInternal(
366 device, DXGI_FORMAT_B8G8R8A8_UNORM,
367 D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
368 }
369
DoesAlphaTextureSharingWork(ID3D11Device * device)370 /* static */ bool D3D11Checks::DoesAlphaTextureSharingWork(
371 ID3D11Device* device) {
372 return DoesTextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM,
373 D3D11_BIND_SHADER_RESOURCE);
374 }
375
GetDxgiDesc(ID3D11Device * device,DXGI_ADAPTER_DESC * out)376 /* static */ bool D3D11Checks::GetDxgiDesc(ID3D11Device* device,
377 DXGI_ADAPTER_DESC* out) {
378 RefPtr<IDXGIDevice> dxgiDevice;
379 HRESULT hr =
380 device->QueryInterface(__uuidof(IDXGIDevice), getter_AddRefs(dxgiDevice));
381 if (FAILED(hr)) {
382 return false;
383 }
384
385 RefPtr<IDXGIAdapter> dxgiAdapter;
386 if (FAILED(dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter)))) {
387 return false;
388 }
389
390 return SUCCEEDED(dxgiAdapter->GetDesc(out));
391 }
392
WarnOnAdapterMismatch(ID3D11Device * device)393 /* static */ void D3D11Checks::WarnOnAdapterMismatch(ID3D11Device* device) {
394 DXGI_ADAPTER_DESC desc;
395 PodZero(&desc);
396 GetDxgiDesc(device, &desc);
397
398 nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
399 nsString vendorID;
400 gfxInfo->GetAdapterVendorID(vendorID);
401 nsresult ec;
402 int32_t vendor = vendorID.ToInteger(&ec, 16);
403 if (vendor != desc.VendorId) {
404 gfxCriticalNote << "VendorIDMismatch V " << hexa(vendor) << " "
405 << hexa(desc.VendorId);
406 }
407 }
408
DoesRemotePresentWork(IDXGIAdapter * adapter)409 /* static */ bool D3D11Checks::DoesRemotePresentWork(IDXGIAdapter* adapter) {
410 // Remote presentation was added in DXGI 1.2, for Windows 8 and the Platform
411 // Update to Windows 7.
412 RefPtr<IDXGIAdapter2> check;
413 HRESULT hr =
414 adapter->QueryInterface(__uuidof(IDXGIAdapter2), getter_AddRefs(check));
415 return SUCCEEDED(hr) && check;
416 }
417
DoesNV12Work(ID3D11Device * device)418 /* static */ bool D3D11Checks::DoesNV12Work(ID3D11Device* device) {
419 if (gfxVars::DXNV12Blocked()) {
420 return false;
421 }
422
423 DXGI_ADAPTER_DESC desc;
424 PodZero(&desc);
425 if (!GetDxgiDesc(device, &desc)) {
426 // Failed to retrieve device information, assume it doesn't work
427 return false;
428 }
429
430 HRESULT hr;
431 UINT formatSupport;
432 hr = device->CheckFormatSupport(DXGI_FORMAT_NV12, &formatSupport);
433 if (FAILED(hr) || !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)) {
434 return false;
435 }
436
437 nsString version;
438 nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
439 if (gfxInfo) {
440 gfxInfo->GetAdapterDriverVersion(version);
441 }
442 return DXVA2Manager::IsNV12Supported(desc.VendorId, desc.DeviceId, version);
443 }
444
445 } // namespace gfx
446 } // namespace mozilla
447