summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/DeviceManagerDx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/DeviceManagerDx.cpp')
-rw-r--r--gfx/thebes/DeviceManagerDx.cpp1432
1 files changed, 1432 insertions, 0 deletions
diff --git a/gfx/thebes/DeviceManagerDx.cpp b/gfx/thebes/DeviceManagerDx.cpp
new file mode 100644
index 0000000000..18c5cea7db
--- /dev/null
+++ b/gfx/thebes/DeviceManagerDx.cpp
@@ -0,0 +1,1432 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DeviceManagerDx.h"
+#include "D3D11Checks.h"
+#include "gfxConfig.h"
+#include "GfxDriverInfo.h"
+#include "gfxWindowsPlatform.h"
+#include "mozilla/D3DMessageUtils.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GraphicsMessages.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/DeviceAttachmentsD3D11.h"
+#include "mozilla/Preferences.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+
+// -
+
+#include <d3d11.h>
+#include <dcomp.h>
+#include <ddraw.h>
+#include <dxgi.h>
+
+namespace mozilla {
+namespace gfx {
+
+using namespace mozilla::widget;
+using namespace mozilla::layers;
+
+StaticAutoPtr<DeviceManagerDx> DeviceManagerDx::sInstance;
+
+// We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
+// since it doesn't include d3d11.h, so we use a static here. It should only
+// be used within InitializeD3D11.
+decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
+
+// It should only be used within CreateDirectCompositionDevice.
+decltype(DCompositionCreateDevice2)* sDcompCreateDevice2Fn = nullptr;
+decltype(DCompositionCreateDevice3)* sDcompCreateDevice3Fn = nullptr;
+
+// It should only be used within CreateDCompSurfaceHandle
+decltype(DCompositionCreateSurfaceHandle)* sDcompCreateSurfaceHandleFn =
+ nullptr;
+
+// We don't have access to the DirectDrawCreateEx type in gfxWindowsPlatform.h,
+// since it doesn't include ddraw.h, so we use a static here. It should only
+// be used within InitializeDirectDrawConfig.
+decltype(DirectDrawCreateEx)* sDirectDrawCreateExFn = nullptr;
+
+/* static */
+void DeviceManagerDx::Init() { sInstance = new DeviceManagerDx(); }
+
+/* static */
+void DeviceManagerDx::Shutdown() { sInstance = nullptr; }
+
+DeviceManagerDx::DeviceManagerDx()
+ : mDeviceLock("gfxWindowsPlatform.mDeviceLock"),
+ mCompositorDeviceSupportsVideo(false) {
+ // Set up the D3D11 feature levels we can ask for.
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
+ mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
+ MOZ_COUNT_CTOR(DeviceManagerDx);
+}
+
+DeviceManagerDx::~DeviceManagerDx() { MOZ_COUNT_DTOR(DeviceManagerDx); }
+
+bool DeviceManagerDx::LoadD3D11() {
+ FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+ MOZ_ASSERT(d3d11.IsEnabled());
+
+ if (sD3D11CreateDeviceFn) {
+ return true;
+ }
+
+ nsModuleHandle module(LoadLibrarySystem32(L"d3d11.dll"));
+ if (!module) {
+ d3d11.SetFailed(FeatureStatus::Unavailable,
+ "Direct3D11 not available on this computer",
+ "FEATURE_FAILURE_D3D11_LIB"_ns);
+ return false;
+ }
+
+ sD3D11CreateDeviceFn =
+ (decltype(D3D11CreateDevice)*)GetProcAddress(module, "D3D11CreateDevice");
+ if (!sD3D11CreateDeviceFn) {
+ // We should just be on Windows Vista or XP in this case.
+ d3d11.SetFailed(FeatureStatus::Unavailable,
+ "Direct3D11 not available on this computer",
+ "FEATURE_FAILURE_D3D11_FUNCPTR"_ns);
+ return false;
+ }
+
+ mD3D11Module.steal(module);
+ return true;
+}
+
+bool DeviceManagerDx::LoadDcomp() {
+ MOZ_ASSERT(gfxConfig::GetFeature(Feature::D3D11_COMPOSITING).IsEnabled());
+ MOZ_ASSERT(gfxVars::UseWebRenderANGLE());
+ MOZ_ASSERT(gfxVars::UseWebRenderDCompWin());
+
+ if (sDcompCreateDevice2Fn) {
+ return true; // Already loaded.
+ }
+
+ nsModuleHandle module(LoadLibrarySystem32(L"dcomp.dll"));
+ if (!module) {
+ return false;
+ }
+
+ sDcompCreateDevice2Fn = (decltype(DCompositionCreateDevice2)*)GetProcAddress(
+ module, "DCompositionCreateDevice2");
+ sDcompCreateDevice3Fn = (decltype(DCompositionCreateDevice3)*)GetProcAddress(
+ module, "DCompositionCreateDevice3");
+ if (!sDcompCreateDevice2Fn) {
+ return false;
+ }
+
+ // Load optional API for external compositing
+ sDcompCreateSurfaceHandleFn =
+ (decltype(DCompositionCreateSurfaceHandle)*)::GetProcAddress(
+ module, "DCompositionCreateSurfaceHandle");
+
+ mDcompModule.steal(module);
+ return true;
+}
+
+void DeviceManagerDx::ReleaseD3D11() {
+ MOZ_ASSERT(!mCompositorDevice);
+ MOZ_ASSERT(!mContentDevice);
+ MOZ_ASSERT(!mVRDevice);
+ MOZ_ASSERT(!mDecoderDevice);
+
+ mD3D11Module.reset();
+ sD3D11CreateDeviceFn = nullptr;
+}
+
+nsTArray<DXGI_OUTPUT_DESC1> DeviceManagerDx::EnumerateOutputs() {
+ RefPtr<IDXGIAdapter> adapter = GetDXGIAdapter();
+
+ if (!adapter) {
+ NS_WARNING("Failed to acquire a DXGI adapter for enumerating outputs.");
+ return nsTArray<DXGI_OUTPUT_DESC1>();
+ }
+
+ nsTArray<DXGI_OUTPUT_DESC1> outputs;
+ for (UINT i = 0;; ++i) {
+ RefPtr<IDXGIOutput> output = nullptr;
+ if (FAILED(adapter->EnumOutputs(i, getter_AddRefs(output)))) {
+ break;
+ }
+
+ RefPtr<IDXGIOutput6> output6 = nullptr;
+ if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput6),
+ getter_AddRefs(output6)))) {
+ break;
+ }
+
+ DXGI_OUTPUT_DESC1 desc;
+ if (FAILED(output6->GetDesc1(&desc))) {
+ break;
+ }
+
+ outputs.AppendElement(desc);
+ }
+ return outputs;
+}
+
+bool DeviceManagerDx::GetOutputFromMonitor(HMONITOR monitor,
+ RefPtr<IDXGIOutput>* aOutOutput) {
+ RefPtr<IDXGIAdapter> adapter = GetDXGIAdapter();
+
+ if (!adapter) {
+ NS_WARNING("Failed to acquire a DXGI adapter for GetOutputFromMonitor.");
+ return false;
+ }
+
+ for (UINT i = 0;; ++i) {
+ RefPtr<IDXGIOutput> output = nullptr;
+ if (FAILED(adapter->EnumOutputs(i, getter_AddRefs(output)))) {
+ break;
+ }
+
+ DXGI_OUTPUT_DESC desc;
+ if (FAILED(output->GetDesc(&desc))) {
+ continue;
+ }
+
+ if (desc.Monitor == monitor) {
+ *aOutOutput = output;
+ return true;
+ }
+ }
+ return false;
+}
+
+void DeviceManagerDx::CheckHardwareStretchingSupport(HwStretchingSupport& aRv) {
+ RefPtr<IDXGIAdapter> adapter = GetDXGIAdapter();
+
+ if (!adapter) {
+ NS_WARNING(
+ "Failed to acquire a DXGI adapter for checking hardware stretching "
+ "support.");
+ ++aRv.mError;
+ return;
+ }
+
+ for (UINT i = 0;; ++i) {
+ RefPtr<IDXGIOutput> output = nullptr;
+ HRESULT result = adapter->EnumOutputs(i, getter_AddRefs(output));
+ if (result == DXGI_ERROR_NOT_FOUND) {
+ // No more outputs to check.
+ break;
+ }
+
+ if (FAILED(result)) {
+ ++aRv.mError;
+ break;
+ }
+
+ RefPtr<IDXGIOutput6> output6 = nullptr;
+ if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput6),
+ getter_AddRefs(output6)))) {
+ ++aRv.mError;
+ continue;
+ }
+
+ UINT flags = 0;
+ if (FAILED(output6->CheckHardwareCompositionSupport(&flags))) {
+ ++aRv.mError;
+ continue;
+ }
+
+ bool fullScreen = flags & DXGI_HARDWARE_COMPOSITION_SUPPORT_FLAG_FULLSCREEN;
+ bool window = flags & DXGI_HARDWARE_COMPOSITION_SUPPORT_FLAG_WINDOWED;
+ if (fullScreen && window) {
+ ++aRv.mBoth;
+ } else if (fullScreen) {
+ ++aRv.mFullScreenOnly;
+ } else if (window) {
+ ++aRv.mWindowOnly;
+ } else {
+ ++aRv.mNone;
+ }
+ }
+}
+
+#ifdef DEBUG
+static inline bool ProcessOwnsCompositor() {
+ return XRE_GetProcessType() == GeckoProcessType_GPU ||
+ XRE_GetProcessType() == GeckoProcessType_VR ||
+ (XRE_IsParentProcess() && !gfxConfig::IsEnabled(Feature::GPU_PROCESS));
+}
+#endif
+
+bool DeviceManagerDx::CreateCompositorDevices() {
+ MutexAutoLock lock(mDeviceLock);
+ return CreateCompositorDevicesLocked();
+}
+
+bool DeviceManagerDx::CreateCompositorDevicesLocked() {
+ MOZ_ASSERT(ProcessOwnsCompositor());
+
+ FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+ MOZ_ASSERT(d3d11.IsEnabled());
+
+ if (int32_t sleepSec =
+ StaticPrefs::gfx_direct3d11_sleep_on_create_device_AtStartup()) {
+ printf_stderr("Attach to PID: %lu\n", GetCurrentProcessId());
+ Sleep(sleepSec * 1000);
+ }
+
+ if (!LoadD3D11()) {
+ return false;
+ }
+
+ CreateCompositorDevice(d3d11);
+
+ if (!d3d11.IsEnabled()) {
+ MOZ_ASSERT(!mCompositorDevice);
+ ReleaseD3D11();
+
+ return false;
+ }
+
+ // We leak these everywhere and we need them our entire runtime anyway, let's
+ // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
+ // as well for D2D1 and device resets.
+ mD3D11Module.disown();
+
+ MOZ_ASSERT(mCompositorDevice);
+ if (!d3d11.IsEnabled()) {
+ return false;
+ }
+
+ // When WR is used, do not preload attachments for D3D11 Non-WR compositor.
+ //
+ // Fallback from WR to D3D11 Non-WR compositor without re-creating gpu process
+ // could happen when WR causes error. In this case, the attachments are loaded
+ // synchronously.
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ PreloadAttachmentsOnCompositorThread();
+ }
+
+ return true;
+}
+
+bool DeviceManagerDx::CreateVRDevice() {
+ MOZ_ASSERT(ProcessOwnsCompositor());
+
+ if (mVRDevice) {
+ return true;
+ }
+
+ if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+ NS_WARNING("Direct3D11 Compositing required for VR");
+ return false;
+ }
+
+ if (!LoadD3D11()) {
+ return false;
+ }
+
+ RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapterLocked();
+ if (!adapter) {
+ NS_WARNING("Failed to acquire a DXGI adapter for VR");
+ return false;
+ }
+
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+
+ HRESULT hr;
+ if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, mVRDevice)) {
+ gfxCriticalError() << "Crash during D3D11 device creation for VR";
+ return false;
+ }
+
+ if (FAILED(hr) || !mVRDevice) {
+ NS_WARNING("Failed to acquire a D3D11 device for VR");
+ return false;
+ }
+
+ return true;
+}
+
+bool DeviceManagerDx::CreateCanvasDevice() {
+ MutexAutoLock lock(mDeviceLock);
+ return CreateCanvasDeviceLocked();
+}
+
+bool DeviceManagerDx::CreateCanvasDeviceLocked() {
+ MOZ_ASSERT(ProcessOwnsCompositor());
+
+ if (mCanvasDevice) {
+ return true;
+ }
+
+ if (!LoadD3D11()) {
+ return false;
+ }
+
+ RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapterLocked();
+ if (!adapter) {
+ NS_WARNING("Failed to acquire a DXGI adapter for Canvas");
+ return false;
+ }
+
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+
+ HRESULT hr;
+ if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr,
+ mCanvasDevice)) {
+ gfxCriticalError() << "Crash during D3D11 device creation for Canvas";
+ return false;
+ }
+
+ if (StaticPrefs::
+ gfx_direct2d_target_independent_rasterization_disabled_AtStartup()) {
+ int creationFlags = 0x2; // disable target independent rasterization
+ const GUID D2D_INTERNAL_DEVICE_CREATION_OPTIONS = {
+ 0xfb3a8e1a,
+ 0x2e3c,
+ 0x4de1,
+ {0x84, 0x42, 0x40, 0x43, 0xe0, 0xb0, 0x94, 0x95}};
+ mCanvasDevice->SetPrivateData(D2D_INTERNAL_DEVICE_CREATION_OPTIONS,
+ sizeof(creationFlags), &creationFlags);
+ }
+
+ if (FAILED(hr) || !mCanvasDevice) {
+ NS_WARNING("Failed to acquire a D3D11 device for Canvas");
+ return false;
+ }
+
+ if (!D3D11Checks::DoesTextureSharingWork(mCanvasDevice)) {
+ mCanvasDevice = nullptr;
+ return false;
+ }
+
+ if (XRE_IsGPUProcess()) {
+ Factory::SetDirect3D11Device(mCanvasDevice);
+ }
+
+ return true;
+}
+
+void DeviceManagerDx::CreateDirectCompositionDevice() {
+ MutexAutoLock lock(mDeviceLock);
+ CreateDirectCompositionDeviceLocked();
+}
+
+void DeviceManagerDx::CreateDirectCompositionDeviceLocked() {
+ if (!gfxVars::UseWebRenderDCompWin()) {
+ return;
+ }
+
+ if (!mCompositorDevice) {
+ return;
+ }
+
+ if (!LoadDcomp()) {
+ return;
+ }
+
+ RefPtr<IDXGIDevice> dxgiDevice;
+ if (mCompositorDevice->QueryInterface(
+ IID_PPV_ARGS((IDXGIDevice**)getter_AddRefs(dxgiDevice))) != S_OK) {
+ return;
+ }
+
+ HRESULT hr;
+ RefPtr<IDCompositionDesktopDevice> desktopDevice;
+ MOZ_SEH_TRY {
+ hr = sDcompCreateDevice3Fn(
+ dxgiDevice.get(),
+ IID_PPV_ARGS(
+ (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice)));
+ if (!desktopDevice) {
+ hr = sDcompCreateDevice2Fn(
+ dxgiDevice.get(),
+ IID_PPV_ARGS(
+ (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice)));
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { return; }
+
+ if (!SUCCEEDED(hr)) {
+ return;
+ }
+
+ RefPtr<IDCompositionDevice2> compositionDevice;
+ if (desktopDevice->QueryInterface(IID_PPV_ARGS(
+ (IDCompositionDevice2**)getter_AddRefs(compositionDevice))) != S_OK) {
+ return;
+ }
+
+ mDirectCompositionDevice = compositionDevice;
+}
+
+/* static */
+HANDLE DeviceManagerDx::CreateDCompSurfaceHandle() {
+ if (!sDcompCreateSurfaceHandleFn) {
+ return 0;
+ }
+
+ HANDLE handle = 0;
+ HRESULT hr = sDcompCreateSurfaceHandleFn(COMPOSITIONOBJECT_ALL_ACCESS,
+ nullptr, &handle);
+ if (FAILED(hr)) {
+ return 0;
+ }
+
+ return handle;
+}
+
+void DeviceManagerDx::ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus) {
+ MOZ_ASSERT(!ProcessOwnsCompositor());
+
+ MutexAutoLock lock(mDeviceLock);
+ mDeviceStatus = Some(aDeviceStatus);
+}
+
+bool DeviceManagerDx::ExportDeviceInfo(D3D11DeviceStatus* aOut) {
+ MutexAutoLock lock(mDeviceLock);
+ if (mDeviceStatus) {
+ *aOut = mDeviceStatus.value();
+ return true;
+ }
+
+ return false;
+}
+
+void DeviceManagerDx::CreateContentDevices() {
+ MutexAutoLock lock(mDeviceLock);
+ CreateContentDevicesLocked();
+}
+
+void DeviceManagerDx::CreateContentDevicesLocked() {
+ MOZ_ASSERT(gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING));
+
+ if (!LoadD3D11()) {
+ return;
+ }
+
+ // We should have been assigned a DeviceStatus from the parent process,
+ // GPU process, or the same process if using in-process compositing.
+ MOZ_ASSERT(mDeviceStatus);
+
+ if (CreateContentDevice() == FeatureStatus::CrashedInHandler) {
+ DisableD3D11AfterCrash();
+ }
+}
+
+already_AddRefed<IDXGIAdapter1> DeviceManagerDx::GetDXGIAdapter() {
+ MutexAutoLock lock(mDeviceLock);
+ return do_AddRef(GetDXGIAdapterLocked());
+}
+
+IDXGIAdapter1* DeviceManagerDx::GetDXGIAdapterLocked() {
+ if (mAdapter) {
+ return mAdapter;
+ }
+
+ nsModuleHandle dxgiModule(LoadLibrarySystem32(L"dxgi.dll"));
+ decltype(CreateDXGIFactory1)* createDXGIFactory1 =
+ (decltype(CreateDXGIFactory1)*)GetProcAddress(dxgiModule,
+ "CreateDXGIFactory1");
+ if (!createDXGIFactory1) {
+ return nullptr;
+ }
+ static const auto fCreateDXGIFactory2 =
+ (decltype(CreateDXGIFactory2)*)GetProcAddress(dxgiModule,
+ "CreateDXGIFactory2");
+
+ // Try to use a DXGI 1.1 adapter in order to share resources
+ // across processes.
+ RefPtr<IDXGIFactory1> factory1;
+ if (StaticPrefs::gfx_direct3d11_enable_debug_layer_AtStartup()) {
+ RefPtr<IDXGIFactory2> factory2;
+ if (fCreateDXGIFactory2) {
+ auto hr = fCreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG,
+ __uuidof(IDXGIFactory2),
+ getter_AddRefs(factory2));
+ MOZ_ALWAYS_TRUE(!FAILED(hr));
+ } else {
+ NS_WARNING(
+ "fCreateDXGIFactory2 not loaded, cannot create debug IDXGIFactory2.");
+ }
+ factory1 = factory2;
+ }
+ if (!factory1) {
+ HRESULT hr =
+ createDXGIFactory1(__uuidof(IDXGIFactory1), getter_AddRefs(factory1));
+ if (FAILED(hr) || !factory1) {
+ // This seems to happen with some people running the iZ3D driver.
+ // They won't get acceleration.
+ return nullptr;
+ }
+ }
+
+ if (!mDeviceStatus) {
+ // If we haven't created a device yet, and have no existing device status,
+ // then this must be the compositor device. Pick the first adapter we can.
+ if (FAILED(factory1->EnumAdapters1(0, getter_AddRefs(mAdapter)))) {
+ return nullptr;
+ }
+ } else {
+ // In the UI and GPU process, we clear mDeviceStatus on device reset, so we
+ // should never reach here. Furthermore, the UI process does not create
+ // devices when using a GPU process.
+ //
+ // So, this should only ever get called on the content process or RDD
+ // process
+ MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsRDDProcess());
+
+ // In the child process, we search for the adapter that matches the parent
+ // process. The first adapter can be mismatched on dual-GPU systems.
+ for (UINT index = 0;; index++) {
+ RefPtr<IDXGIAdapter1> adapter;
+ if (FAILED(factory1->EnumAdapters1(index, getter_AddRefs(adapter)))) {
+ break;
+ }
+
+ const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
+
+ DXGI_ADAPTER_DESC desc;
+ if (SUCCEEDED(adapter->GetDesc(&desc)) &&
+ desc.AdapterLuid.HighPart == preferred.AdapterLuid.HighPart &&
+ desc.AdapterLuid.LowPart == preferred.AdapterLuid.LowPart &&
+ desc.VendorId == preferred.VendorId &&
+ desc.DeviceId == preferred.DeviceId) {
+ mAdapter = adapter.forget();
+ break;
+ }
+ }
+ }
+
+ if (!mAdapter) {
+ return nullptr;
+ }
+
+ // We leak this module everywhere, we might as well do so here as well.
+ dxgiModule.disown();
+ return mAdapter;
+}
+
+bool DeviceManagerDx::CreateCompositorDeviceHelper(
+ FeatureState& aD3d11, IDXGIAdapter1* aAdapter, bool aAttemptVideoSupport,
+ RefPtr<ID3D11Device>& aOutDevice) {
+ // Check if a failure was injected for testing.
+ if (StaticPrefs::gfx_testing_device_fail()) {
+ aD3d11.SetFailed(FeatureStatus::Failed,
+ "Direct3D11 device failure simulated by preference",
+ "FEATURE_FAILURE_D3D11_SIM"_ns);
+ return false;
+ }
+
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+
+ DXGI_ADAPTER_DESC desc;
+ aAdapter->GetDesc(&desc);
+ if (desc.VendorId != 0x1414) {
+ // 0x1414 is Microsoft (e.g. WARP)
+ // When not using WARP, use
+ // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS to prevent
+ // bug 1092260. IE 11 also uses this flag.
+ flags |= D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS;
+ }
+
+ if (aAttemptVideoSupport) {
+ flags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+ }
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+ if (!CreateDevice(aAdapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
+ if (!aAttemptVideoSupport) {
+ gfxCriticalError() << "Crash during D3D11 device creation";
+ aD3d11.SetFailed(FeatureStatus::CrashedInHandler,
+ "Crashed trying to acquire a D3D11 device",
+ "FEATURE_FAILURE_D3D11_DEVICE1"_ns);
+ }
+ return false;
+ }
+
+ if (FAILED(hr) || !device) {
+ if (!aAttemptVideoSupport) {
+ aD3d11.SetFailed(FeatureStatus::Failed,
+ "Failed to acquire a D3D11 device",
+ "FEATURE_FAILURE_D3D11_DEVICE2"_ns);
+ }
+ return false;
+ }
+ if (!D3D11Checks::DoesDeviceWork()) {
+ if (!aAttemptVideoSupport) {
+ aD3d11.SetFailed(FeatureStatus::Broken,
+ "Direct3D11 device was determined to be broken",
+ "FEATURE_FAILURE_D3D11_BROKEN"_ns);
+ }
+ return false;
+ }
+
+ aOutDevice = device;
+ return true;
+}
+
+// Note that it's enough for us to just use a counter for a unique ID,
+// even though the counter isn't synchronized between processes. If we
+// start in the GPU process and wind up in the parent process, the
+// whole graphics stack is blown away anyway. But just in case, we
+// make gpu process IDs negative and parent process IDs positive.
+static inline int32_t GetNextDeviceCounter() {
+ static int32_t sDeviceCounter = 0;
+ return XRE_IsGPUProcess() ? --sDeviceCounter : ++sDeviceCounter;
+}
+
+void DeviceManagerDx::CreateCompositorDevice(FeatureState& d3d11) {
+ if (StaticPrefs::layers_d3d11_force_warp_AtStartup()) {
+ CreateWARPCompositorDevice();
+ return;
+ }
+
+ RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapterLocked();
+ if (!adapter) {
+ d3d11.SetFailed(FeatureStatus::Unavailable,
+ "Failed to acquire a DXGI adapter",
+ "FEATURE_FAILURE_D3D11_DXGI"_ns);
+ return;
+ }
+
+ if (XRE_IsGPUProcess() && !D3D11Checks::DoesRemotePresentWork(adapter)) {
+ d3d11.SetFailed(FeatureStatus::Unavailable,
+ "DXGI does not support out-of-process presentation",
+ "FEATURE_FAILURE_D3D11_REMOTE_PRESENT"_ns);
+ return;
+ }
+
+ RefPtr<ID3D11Device> device;
+ if (!CreateCompositorDeviceHelper(d3d11, adapter, true, device)) {
+ // Try again without video support and record that it failed.
+ mCompositorDeviceSupportsVideo = false;
+ if (!CreateCompositorDeviceHelper(d3d11, adapter, false, device)) {
+ return;
+ }
+ } else {
+ mCompositorDeviceSupportsVideo = true;
+ }
+
+ // Only test this when not using WARP since it can fail and cause
+ // GetDeviceRemovedReason to return weird values.
+ bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
+
+ DXGI_ADAPTER_DESC desc;
+ PodZero(&desc);
+ adapter->GetDesc(&desc);
+
+ if (!textureSharingWorks) {
+ gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE, FeatureStatus::Broken,
+ "Texture sharing doesn't work",
+ "FEATURE_FAILURE_HW_ANGLE_NEEDS_TEXTURE_SHARING"_ns);
+ }
+ if (D3D11Checks::DoesRenderTargetViewNeedRecreating(device)) {
+ gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE, FeatureStatus::Broken,
+ "RenderTargetViews need recreating",
+ "FEATURE_FAILURE_HW_ANGLE_NEEDS_RTV_RECREATION"_ns);
+ }
+ if (XRE_IsParentProcess()) {
+ // It seems like this may only happen when we're using the NVIDIA gpu
+ D3D11Checks::WarnOnAdapterMismatch(device);
+ }
+
+ RefPtr<ID3D10Multithread> multi;
+ HRESULT hr = device->QueryInterface(__uuidof(ID3D10Multithread),
+ getter_AddRefs(multi));
+ if (SUCCEEDED(hr) && multi) {
+ multi->SetMultithreadProtected(TRUE);
+ }
+
+ uint32_t featureLevel = device->GetFeatureLevel();
+ auto formatOptions = D3D11Checks::FormatOptions(device);
+ mCompositorDevice = device;
+
+ int32_t sequenceNumber = GetNextDeviceCounter();
+ mDeviceStatus = Some(D3D11DeviceStatus(
+ false, textureSharingWorks, featureLevel, DxgiAdapterDesc::From(desc),
+ sequenceNumber, formatOptions));
+ mCompositorDevice->SetExceptionMode(0);
+}
+
+bool DeviceManagerDx::CreateDevice(IDXGIAdapter* aAdapter,
+ D3D_DRIVER_TYPE aDriverType, UINT aFlags,
+ HRESULT& aResOut,
+ RefPtr<ID3D11Device>& aOutDevice) {
+ if (StaticPrefs::gfx_direct3d11_enable_debug_layer_AtStartup() ||
+ StaticPrefs::gfx_direct3d11_break_on_error_AtStartup()) {
+ aFlags |= D3D11_CREATE_DEVICE_DEBUG;
+ }
+
+ MOZ_SEH_TRY {
+ aResOut = sD3D11CreateDeviceFn(
+ aAdapter, aDriverType, nullptr, aFlags, mFeatureLevels.Elements(),
+ mFeatureLevels.Length(), D3D11_SDK_VERSION, getter_AddRefs(aOutDevice),
+ nullptr, nullptr);
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { return false; }
+
+ if (StaticPrefs::gfx_direct3d11_break_on_error_AtStartup()) {
+ do {
+ if (!aOutDevice) break;
+
+ RefPtr<ID3D11Debug> debug;
+ if (!SUCCEEDED(aOutDevice->QueryInterface(__uuidof(ID3D11Debug),
+ getter_AddRefs(debug))))
+ break;
+
+ RefPtr<ID3D11InfoQueue> infoQueue;
+ if (!SUCCEEDED(debug->QueryInterface(__uuidof(ID3D11InfoQueue),
+ getter_AddRefs(infoQueue))))
+ break;
+
+ D3D11_INFO_QUEUE_FILTER filter;
+ PodZero(&filter);
+
+ // Disable warnings caused by Advanced Layers that are known and not
+ // problematic.
+ D3D11_MESSAGE_ID blockIDs[] = {
+ D3D11_MESSAGE_ID_DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL};
+ filter.DenyList.NumIDs = MOZ_ARRAY_LENGTH(blockIDs);
+ filter.DenyList.pIDList = blockIDs;
+ infoQueue->PushStorageFilter(&filter);
+
+ infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
+ infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
+ infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, true);
+ } while (false);
+ }
+
+ return true;
+}
+
+void DeviceManagerDx::CreateWARPCompositorDevice() {
+ ScopedGfxFeatureReporter reporterWARP(
+ "D3D11-WARP", StaticPrefs::layers_d3d11_force_warp_AtStartup());
+ FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+
+ // Use D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
+ // to prevent bug 1092260. IE 11 also uses this flag.
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+ if (!CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, flags, hr, device)) {
+ gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
+ d3d11.SetFailed(FeatureStatus::CrashedInHandler,
+ "Crashed creating a D3D11 WARP device",
+ "FEATURE_FAILURE_D3D11_WARP_DEVICE"_ns);
+ }
+
+ if (FAILED(hr) || !device) {
+ // This should always succeed... in theory.
+ gfxCriticalError() << "Failed to initialize WARP D3D11 device! "
+ << hexa(hr);
+ d3d11.SetFailed(FeatureStatus::Failed,
+ "Failed to create a D3D11 WARP device",
+ "FEATURE_FAILURE_D3D11_WARP_DEVICE2"_ns);
+ return;
+ }
+
+ bool textureSharingWorks = D3D11Checks::DoesTextureSharingWork(device);
+
+ RefPtr<ID3D10Multithread> multi;
+ hr = device->QueryInterface(__uuidof(ID3D10Multithread),
+ getter_AddRefs(multi));
+ if (SUCCEEDED(hr) && multi) {
+ multi->SetMultithreadProtected(TRUE);
+ }
+
+ DXGI_ADAPTER_DESC desc;
+ D3D11Checks::GetDxgiDesc(device, &desc);
+
+ int featureLevel = device->GetFeatureLevel();
+
+ auto formatOptions = D3D11Checks::FormatOptions(device);
+ mCompositorDevice = device;
+
+ int32_t sequenceNumber = GetNextDeviceCounter();
+ mDeviceStatus = Some(D3D11DeviceStatus(
+ true, textureSharingWorks, featureLevel, DxgiAdapterDesc::From(desc),
+ sequenceNumber, formatOptions));
+ mCompositorDevice->SetExceptionMode(0);
+
+ reporterWARP.SetSuccessful();
+}
+
+FeatureStatus DeviceManagerDx::CreateContentDevice() {
+ RefPtr<IDXGIAdapter1> adapter;
+ if (!mDeviceStatus->isWARP()) {
+ adapter = GetDXGIAdapterLocked();
+ if (!adapter) {
+ gfxCriticalNote << "Could not get a DXGI adapter";
+ return FeatureStatus::Unavailable;
+ }
+ }
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+
+ UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+ D3D_DRIVER_TYPE type =
+ mDeviceStatus->isWARP() ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN;
+ if (!CreateDevice(adapter, type, flags, hr, device)) {
+ gfxCriticalNote
+ << "Recovered from crash while creating a D3D11 content device";
+ gfxWindowsPlatform::RecordContentDeviceFailure(
+ TelemetryDeviceCode::Content);
+ return FeatureStatus::CrashedInHandler;
+ }
+
+ if (FAILED(hr) || !device) {
+ gfxCriticalNote << "Failed to create a D3D11 content device: " << hexa(hr);
+ gfxWindowsPlatform::RecordContentDeviceFailure(
+ TelemetryDeviceCode::Content);
+ return FeatureStatus::Failed;
+ }
+
+ // InitializeD2D() will abort early if the compositor device did not support
+ // texture sharing. If we're in the content process, we can't rely on the
+ // parent device alone: some systems have dual GPUs that are capable of
+ // binding the parent and child processes to different GPUs. As a safety net,
+ // we re-check texture sharing against the newly created D3D11 content device.
+ // If it fails, we won't use Direct2D.
+ if (XRE_IsContentProcess()) {
+ if (!D3D11Checks::DoesTextureSharingWork(device)) {
+ return FeatureStatus::Failed;
+ }
+
+ DebugOnly<bool> ok = ContentAdapterIsParentAdapter(device);
+ MOZ_ASSERT(ok);
+ }
+
+ mContentDevice = device;
+ mContentDevice->SetExceptionMode(0);
+
+ RefPtr<ID3D10Multithread> multi;
+ hr = mContentDevice->QueryInterface(__uuidof(ID3D10Multithread),
+ getter_AddRefs(multi));
+ if (SUCCEEDED(hr) && multi) {
+ multi->SetMultithreadProtected(TRUE);
+ }
+ return FeatureStatus::Available;
+}
+
+RefPtr<ID3D11Device> DeviceManagerDx::CreateDecoderDevice(
+ bool aHardwareWebRender) {
+ MutexAutoLock lock(mDeviceLock);
+
+ if (!mDeviceStatus) {
+ return nullptr;
+ }
+
+ bool isAMD = mDeviceStatus->adapter().VendorId == 0x1002;
+ bool reuseDevice = false;
+ if (gfxVars::ReuseDecoderDevice()) {
+ reuseDevice = true;
+ } else if (isAMD) {
+ reuseDevice = true;
+ gfxCriticalNoteOnce << "Always have to reuse decoder device on AMD";
+ }
+
+ if (reuseDevice) {
+ // Use mCompositorDevice for decoder device only for hardware WebRender.
+ if (aHardwareWebRender && mCompositorDevice &&
+ mCompositorDeviceSupportsVideo && !mDecoderDevice) {
+ mDecoderDevice = mCompositorDevice;
+
+ RefPtr<ID3D10Multithread> multi;
+ mDecoderDevice->QueryInterface(__uuidof(ID3D10Multithread),
+ getter_AddRefs(multi));
+ if (multi) {
+ MOZ_ASSERT(multi->GetMultithreadProtected());
+ }
+ }
+
+ if (mDecoderDevice) {
+ RefPtr<ID3D11Device> dev = mDecoderDevice;
+ return dev.forget();
+ }
+ }
+
+ if (!sD3D11CreateDeviceFn) {
+ // We should just be on Windows Vista or XP in this case.
+ return nullptr;
+ }
+
+ RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapterLocked();
+ if (!adapter) {
+ return nullptr;
+ }
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+
+ UINT flags = D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS |
+ D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+ if (!CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, flags, hr, device)) {
+ return nullptr;
+ }
+ if (FAILED(hr) || !device || !D3D11Checks::DoesDeviceWork()) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D10Multithread> multi;
+ device->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+ if (multi) {
+ multi->SetMultithreadProtected(TRUE);
+ }
+ if (reuseDevice) {
+ mDecoderDevice = device;
+ }
+ return device;
+}
+
+// ID3D11DeviceChild, IDXGIObject and ID3D11Device implement SetPrivateData with
+// the exact same parameters.
+template <typename T>
+static HRESULT SetDebugName(T* d3d11Object, const char* debugString) {
+ return d3d11Object->SetPrivateData(WKPDID_D3DDebugObjectName,
+ strlen(debugString), debugString);
+}
+
+RefPtr<ID3D11Device> DeviceManagerDx::CreateMediaEngineDevice() {
+ MutexAutoLock lock(mDeviceLock);
+ if (!LoadD3D11()) {
+ return nullptr;
+ }
+
+ HRESULT hr;
+ RefPtr<ID3D11Device> device;
+ UINT flags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT |
+ D3D11_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS;
+ if (!CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, flags, hr, device)) {
+ return nullptr;
+ }
+ if (FAILED(hr) || !device || !D3D11Checks::DoesDeviceWork()) {
+ return nullptr;
+ }
+ Unused << SetDebugName(device.get(), "MFMediaEngineDevice");
+
+ RefPtr<ID3D10Multithread> multi;
+ device->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+ if (multi) {
+ multi->SetMultithreadProtected(TRUE);
+ }
+ return device;
+}
+
+void DeviceManagerDx::ResetDevices() {
+ MutexAutoLock lock(mDeviceLock);
+ ResetDevicesLocked();
+}
+
+void DeviceManagerDx::ResetDevicesLocked() {
+ mAdapter = nullptr;
+ mCompositorAttachments = nullptr;
+ mCompositorDevice = nullptr;
+ mContentDevice = nullptr;
+ mCanvasDevice = nullptr;
+ mImageDevice = nullptr;
+ mVRDevice = nullptr;
+ mDecoderDevice = nullptr;
+ mDirectCompositionDevice = nullptr;
+ mDeviceStatus = Nothing();
+ mDeviceResetReason = Nothing();
+ Factory::SetDirect3D11Device(nullptr);
+}
+
+bool DeviceManagerDx::MaybeResetAndReacquireDevices() {
+ MutexAutoLock lock(mDeviceLock);
+
+ DeviceResetReason resetReason;
+ if (!HasDeviceResetLocked(&resetReason)) {
+ return false;
+ }
+
+ GPUProcessManager::RecordDeviceReset(resetReason);
+
+ bool createCompositorDevice = !!mCompositorDevice;
+ bool createContentDevice = !!mContentDevice;
+ bool createCanvasDevice = !!mCanvasDevice;
+ bool createDirectCompositionDevice = !!mDirectCompositionDevice;
+
+ ResetDevicesLocked();
+
+ if (createCompositorDevice && !CreateCompositorDevicesLocked()) {
+ // Just stop, don't try anything more
+ return true;
+ }
+ if (createContentDevice) {
+ CreateContentDevicesLocked();
+ }
+ if (createCanvasDevice) {
+ CreateCanvasDeviceLocked();
+ }
+ if (createDirectCompositionDevice) {
+ CreateDirectCompositionDeviceLocked();
+ }
+
+ return true;
+}
+
+bool DeviceManagerDx::ContentAdapterIsParentAdapter(ID3D11Device* device) {
+ DXGI_ADAPTER_DESC desc;
+ if (!D3D11Checks::GetDxgiDesc(device, &desc)) {
+ gfxCriticalNote << "Could not query device DXGI adapter info";
+ return false;
+ }
+
+ const DxgiAdapterDesc& preferred = mDeviceStatus->adapter();
+
+ if (desc.VendorId != preferred.VendorId ||
+ desc.DeviceId != preferred.DeviceId ||
+ desc.SubSysId != preferred.SubSysId ||
+ desc.AdapterLuid.HighPart != preferred.AdapterLuid.HighPart ||
+ desc.AdapterLuid.LowPart != preferred.AdapterLuid.LowPart) {
+ gfxCriticalNote << "VendorIDMismatch P " << hexa(preferred.VendorId) << " "
+ << hexa(desc.VendorId);
+ return false;
+ }
+
+ return true;
+}
+
+static DeviceResetReason HResultToResetReason(HRESULT hr) {
+ switch (hr) {
+ case DXGI_ERROR_DEVICE_HUNG:
+ return DeviceResetReason::HUNG;
+ case DXGI_ERROR_DEVICE_REMOVED:
+ return DeviceResetReason::REMOVED;
+ case DXGI_ERROR_DEVICE_RESET:
+ return DeviceResetReason::RESET;
+ case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+ return DeviceResetReason::DRIVER_ERROR;
+ case DXGI_ERROR_INVALID_CALL:
+ return DeviceResetReason::INVALID_CALL;
+ case E_OUTOFMEMORY:
+ return DeviceResetReason::OUT_OF_MEMORY;
+ default:
+ MOZ_ASSERT(false);
+ }
+ return DeviceResetReason::OTHER;
+}
+
+bool DeviceManagerDx::HasDeviceReset(DeviceResetReason* aOutReason) {
+ MutexAutoLock lock(mDeviceLock);
+ return HasDeviceResetLocked(aOutReason);
+}
+
+bool DeviceManagerDx::HasDeviceResetLocked(DeviceResetReason* aOutReason) {
+ if (mDeviceResetReason) {
+ if (aOutReason) {
+ *aOutReason = mDeviceResetReason.value();
+ }
+ return true;
+ }
+
+ DeviceResetReason reason;
+ if (GetAnyDeviceRemovedReason(&reason)) {
+ mDeviceResetReason = Some(reason);
+ if (aOutReason) {
+ *aOutReason = reason;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool DidDeviceReset(const RefPtr<ID3D11Device>& aDevice,
+ DeviceResetReason* aOutReason) {
+ if (!aDevice) {
+ return false;
+ }
+ HRESULT hr = aDevice->GetDeviceRemovedReason();
+ if (hr == S_OK) {
+ return false;
+ }
+
+ *aOutReason = HResultToResetReason(hr);
+ return true;
+}
+
+bool DeviceManagerDx::GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason) {
+ if (DidDeviceReset(mCompositorDevice, aOutReason) ||
+ DidDeviceReset(mContentDevice, aOutReason) ||
+ DidDeviceReset(mCanvasDevice, aOutReason)) {
+ return true;
+ }
+
+ if (XRE_IsParentProcess() && NS_IsMainThread() &&
+ StaticPrefs::gfx_testing_device_reset()) {
+ Preferences::SetInt("gfx.testing.device-reset", 0);
+ *aOutReason = DeviceResetReason::FORCED_RESET;
+ return true;
+ }
+
+ return false;
+}
+
+void DeviceManagerDx::ForceDeviceReset(ForcedDeviceResetReason aReason) {
+ Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON,
+ uint32_t(aReason));
+ {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceResetReason) {
+ mDeviceResetReason = Some(DeviceResetReason::FORCED_RESET);
+ }
+ }
+}
+
+void DeviceManagerDx::DisableD3D11AfterCrash() {
+ gfxConfig::Disable(Feature::D3D11_COMPOSITING,
+ FeatureStatus::CrashedInHandler,
+ "Crashed while acquiring a Direct3D11 device",
+ "FEATURE_FAILURE_D3D11_CRASH"_ns);
+ ResetDevices();
+}
+
+RefPtr<ID3D11Device> DeviceManagerDx::GetCompositorDevice() {
+ /// ID3D11Device is thread-safe. We need the lock to read the
+ /// mDeviceLockPointer, but manipulating the pointee outside of the lock is
+ /// safe. See
+ /// https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-render-multi-thread-intro
+ MutexAutoLock lock(mDeviceLock);
+ return mCompositorDevice;
+}
+
+RefPtr<ID3D11Device> DeviceManagerDx::GetContentDevice() {
+ MOZ_ASSERT(XRE_IsGPUProcess() ||
+ gfxPlatform::GetPlatform()->DevicesInitialized());
+
+ MutexAutoLock lock(mDeviceLock);
+ return mContentDevice;
+}
+
+RefPtr<ID3D11Device> DeviceManagerDx::GetImageDevice() {
+ MutexAutoLock lock(mDeviceLock);
+ if (mImageDevice) {
+ return mImageDevice;
+ }
+
+ RefPtr<ID3D11Device> device = mContentDevice;
+ if (!device) {
+ device = mCompositorDevice;
+ }
+
+ if (!device) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D10Multithread> multi;
+ HRESULT hr =
+ device->QueryInterface((ID3D10Multithread**)getter_AddRefs(multi));
+ if (FAILED(hr) || !multi) {
+ gfxWarning() << "Multithread safety interface not supported. " << hr;
+ return nullptr;
+ } else {
+ MOZ_ASSERT(multi->GetMultithreadProtected());
+ }
+
+ mImageDevice = device;
+
+ return mImageDevice;
+}
+
+RefPtr<ID3D11Device> DeviceManagerDx::GetVRDevice() {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mVRDevice) {
+ CreateVRDevice();
+ }
+ return mVRDevice;
+}
+
+RefPtr<ID3D11Device> DeviceManagerDx::GetCanvasDevice() {
+ MutexAutoLock lock(mDeviceLock);
+ return mCanvasDevice;
+}
+
+RefPtr<IDCompositionDevice2> DeviceManagerDx::GetDirectCompositionDevice() {
+ MutexAutoLock lock(mDeviceLock);
+ return mDirectCompositionDevice;
+}
+
+unsigned DeviceManagerDx::GetCompositorFeatureLevel() const {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return 0;
+ }
+ return mDeviceStatus->featureLevel();
+}
+
+bool DeviceManagerDx::TextureSharingWorks() {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ return mDeviceStatus->textureSharingWorks();
+}
+
+bool DeviceManagerDx::CanInitializeKeyedMutexTextures() {
+ MutexAutoLock lock(mDeviceLock);
+ return mDeviceStatus && StaticPrefs::gfx_direct3d11_allow_keyed_mutex() &&
+ gfxVars::AllowD3D11KeyedMutex();
+}
+
+bool DeviceManagerDx::IsWARP() {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ return mDeviceStatus->isWARP();
+}
+
+bool DeviceManagerDx::CanUseNV12() {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ return mDeviceStatus->formatOptions().contains(
+ D3D11Checks::VideoFormatOption::NV12);
+}
+
+bool DeviceManagerDx::CanUseP010() {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ return mDeviceStatus->formatOptions().contains(
+ D3D11Checks::VideoFormatOption::P010);
+}
+
+bool DeviceManagerDx::CanUseP016() {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mDeviceStatus) {
+ return false;
+ }
+ return mDeviceStatus->formatOptions().contains(
+ D3D11Checks::VideoFormatOption::P016);
+}
+
+bool DeviceManagerDx::CanUseDComp() {
+ MutexAutoLock lock(mDeviceLock);
+ return !!mDirectCompositionDevice;
+}
+
+void DeviceManagerDx::InitializeDirectDraw() {
+ MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
+
+ if (mDirectDraw) {
+ // Already initialized.
+ return;
+ }
+
+ FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
+ if (!ddraw.IsEnabled()) {
+ return;
+ }
+
+ // Check if DirectDraw is available on this system.
+ mDirectDrawDLL.own(LoadLibrarySystem32(L"ddraw.dll"));
+ if (!mDirectDrawDLL) {
+ ddraw.SetFailed(FeatureStatus::Unavailable,
+ "DirectDraw not available on this computer",
+ "FEATURE_FAILURE_DDRAW_LIB"_ns);
+ return;
+ }
+
+ sDirectDrawCreateExFn = (decltype(DirectDrawCreateEx)*)GetProcAddress(
+ mDirectDrawDLL, "DirectDrawCreateEx");
+ if (!sDirectDrawCreateExFn) {
+ ddraw.SetFailed(FeatureStatus::Unavailable,
+ "DirectDraw not available on this computer",
+ "FEATURE_FAILURE_DDRAW_LIB"_ns);
+ return;
+ }
+
+ HRESULT hr;
+ MOZ_SEH_TRY {
+ hr = sDirectDrawCreateExFn(nullptr, getter_AddRefs(mDirectDraw),
+ IID_IDirectDraw7, nullptr);
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
+ "FEATURE_FAILURE_DDRAW_LIB"_ns);
+ gfxCriticalNote << "DoesCreatingDirectDrawFailed";
+ return;
+ }
+ if (FAILED(hr)) {
+ ddraw.SetFailed(FeatureStatus::Failed, "Failed to create DirectDraw",
+ "FEATURE_FAILURE_DDRAW_LIB"_ns);
+ gfxCriticalNote << "DoesCreatingDirectDrawFailed " << hexa(hr);
+ return;
+ }
+}
+
+IDirectDraw7* DeviceManagerDx::GetDirectDraw() { return mDirectDraw; }
+
+void DeviceManagerDx::GetCompositorDevices(
+ RefPtr<ID3D11Device>* aOutDevice,
+ RefPtr<layers::DeviceAttachmentsD3D11>* aOutAttachments) {
+ RefPtr<ID3D11Device> device;
+ {
+ MutexAutoLock lock(mDeviceLock);
+ if (!mCompositorDevice) {
+ return;
+ }
+ if (mCompositorAttachments) {
+ *aOutDevice = mCompositorDevice;
+ *aOutAttachments = mCompositorAttachments;
+ return;
+ }
+
+ // Otherwise, we'll try to create attachments outside the lock.
+ device = mCompositorDevice;
+ }
+
+ // We save the attachments object even if it fails to initialize, so the
+ // compositor can grab the failure ID.
+ RefPtr<layers::DeviceAttachmentsD3D11> attachments =
+ layers::DeviceAttachmentsD3D11::Create(device);
+ {
+ MutexAutoLock lock(mDeviceLock);
+ if (device != mCompositorDevice) {
+ return;
+ }
+ mCompositorAttachments = attachments;
+ }
+
+ *aOutDevice = device;
+ *aOutAttachments = attachments;
+}
+
+/* static */
+void DeviceManagerDx::PreloadAttachmentsOnCompositorThread() {
+ if (!CompositorThread()) {
+ return;
+ }
+
+ RefPtr<Runnable> task = NS_NewRunnableFunction(
+ "DeviceManagerDx::PreloadAttachmentsOnCompositorThread", []() -> void {
+ if (DeviceManagerDx* dm = DeviceManagerDx::Get()) {
+ RefPtr<ID3D11Device> device;
+ RefPtr<layers::DeviceAttachmentsD3D11> attachments;
+ dm->GetCompositorDevices(&device, &attachments);
+ }
+ });
+ CompositorThread()->Dispatch(task.forget());
+}
+
+} // namespace gfx
+} // namespace mozilla