tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

D3D11Checks.cpp (16950B)


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