// Copyright 2010 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "VideoBackends/D3D/D3DBase.h" #include #include #include "Common/CommonTypes.h" #include "Common/DynamicLibrary.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Core/Config/GraphicsSettings.h" #include "Core/ConfigManager.h" #include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/DXTexture.h" #include "VideoBackends/D3DCommon/D3DCommon.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoConfig.h" namespace DX11 { static Common::DynamicLibrary s_d3d11_library; namespace D3D { ComPtr dxgi_factory; ComPtr device; ComPtr device1; ComPtr context; D3D_FEATURE_LEVEL feature_level; static ComPtr s_debug; constexpr std::array s_supported_feature_levels{ D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, }; bool Create(u32 adapter_index, bool enable_debug_layer) { PFN_D3D11_CREATE_DEVICE d3d11_create_device; if (!s_d3d11_library.Open("d3d11.dll") || !s_d3d11_library.GetSymbol("D3D11CreateDevice", &d3d11_create_device)) { PanicAlertFmtT("Failed to load d3d11.dll"); s_d3d11_library.Close(); return false; } if (!D3DCommon::LoadLibraries()) { s_d3d11_library.Close(); return false; } dxgi_factory = D3DCommon::CreateDXGIFactory(enable_debug_layer); if (!dxgi_factory) { PanicAlertFmtT("Failed to create DXGI factory"); D3DCommon::UnloadLibraries(); s_d3d11_library.Close(); return false; } ComPtr adapter; HRESULT hr = dxgi_factory->EnumAdapters(adapter_index, adapter.GetAddressOf()); if (FAILED(hr)) { WARN_LOG_FMT(VIDEO, "Adapter {} not found, using default: {}", adapter_index, DX11HRWrap(hr)); adapter = nullptr; } // Creating debug devices can sometimes fail if the user doesn't have the correct // version of the DirectX SDK. If it does, simply fallback to a non-debug device. if (enable_debug_layer) { hr = d3d11_create_device( adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_DEBUG, s_supported_feature_levels.data(), static_cast(s_supported_feature_levels.size()), D3D11_SDK_VERSION, device.GetAddressOf(), &feature_level, context.GetAddressOf()); // Debugbreak on D3D error if (SUCCEEDED(hr) && SUCCEEDED(hr = device.As(&s_debug))) { ComPtr info_queue; if (SUCCEEDED(s_debug.As(&info_queue))) { info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true); info_queue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true); D3D11_MESSAGE_ID hide[] = {D3D11_MESSAGE_ID_SETPRIVATEDATA_CHANGINGPARAMS}; D3D11_INFO_QUEUE_FILTER filter = {}; filter.DenyList.NumIDs = sizeof(hide) / sizeof(D3D11_MESSAGE_ID); filter.DenyList.pIDList = hide; info_queue->AddStorageFilterEntries(&filter); } } else { WARN_LOG_FMT(VIDEO, "Debug layer requested but not available: {}", DX11HRWrap(hr)); } } if (!enable_debug_layer || FAILED(hr)) { hr = d3d11_create_device( adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, s_supported_feature_levels.data(), static_cast(s_supported_feature_levels.size()), D3D11_SDK_VERSION, device.GetAddressOf(), &feature_level, context.GetAddressOf()); } if (FAILED(hr)) { PanicAlertFmtT( "Failed to initialize Direct3D.\nMake sure your video card supports at least D3D 10.0\n{0}", DX11HRWrap(hr)); dxgi_factory.Reset(); D3DCommon::UnloadLibraries(); s_d3d11_library.Close(); return false; } hr = device.As(&device1); if (FAILED(hr)) { WARN_LOG_FMT(VIDEO, "Missing Direct3D 11.1 support. Logical operations will not be supported.\n{}", DX11HRWrap(hr)); } stateman = std::make_unique(); return true; } void Destroy() { stateman.reset(); context->ClearState(); context->Flush(); context.Reset(); device1.Reset(); auto remaining_references = device.Reset(); if (s_debug) { --remaining_references; // the debug interface increases the refcount of the device, subtract // that. if (remaining_references) { // print out alive objects, but only if we actually have pending references // note this will also print out internal live objects to the debug console s_debug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL); } s_debug.Reset(); } if (remaining_references) ERROR_LOG_FMT(VIDEO, "Unreleased references: {}.", remaining_references); else NOTICE_LOG_FMT(VIDEO, "Successfully released all device references!"); dxgi_factory.Reset(); D3DCommon::UnloadLibraries(); s_d3d11_library.Close(); } std::vector GetAAModes(u32 adapter_index) { // Use temporary device if we don't have one already. Common::DynamicLibrary temp_lib; ComPtr temp_device = device; D3D_FEATURE_LEVEL temp_feature_level = feature_level; if (!temp_device) { ComPtr temp_dxgi_factory = D3DCommon::CreateDXGIFactory(false); if (!temp_dxgi_factory) return {}; ComPtr adapter; temp_dxgi_factory->EnumAdapters(adapter_index, adapter.GetAddressOf()); PFN_D3D11_CREATE_DEVICE d3d11_create_device; if (!temp_lib.Open("d3d11.dll") || !temp_lib.GetSymbol("D3D11CreateDevice", &d3d11_create_device)) { return {}; } HRESULT hr = d3d11_create_device( adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, s_supported_feature_levels.data(), static_cast(s_supported_feature_levels.size()), D3D11_SDK_VERSION, temp_device.GetAddressOf(), &temp_feature_level, nullptr); if (FAILED(hr)) return {}; } // NOTE: D3D 10.0 doesn't support multisampled resources which are bound as depth buffers AND // shader resources. Thus, we can't have MSAA with 10.0 level hardware. if (temp_feature_level == D3D_FEATURE_LEVEL_10_0) return {}; const DXGI_FORMAT target_format = D3DCommon::GetDXGIFormatForAbstractFormat(FramebufferManager::GetEFBColorFormat(), false); std::vector aa_modes; for (u32 samples = 1; samples <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; ++samples) { UINT quality_levels = 0; if (SUCCEEDED( temp_device->CheckMultisampleQualityLevels(target_format, samples, &quality_levels)) && quality_levels > 0) { aa_modes.push_back(samples); } } return aa_modes; } bool SupportsTextureFormat(DXGI_FORMAT format) { UINT support; if (FAILED(device->CheckFormatSupport(format, &support))) return false; return (support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0; } bool SupportsLogicOp(u32 adapter_index) { // Use temporary device if we don't have one already. Common::DynamicLibrary temp_lib; ComPtr temp_device1 = device1; if (!device) { ComPtr temp_device; ComPtr temp_dxgi_factory = D3DCommon::CreateDXGIFactory(false); if (!temp_dxgi_factory) return false; ComPtr adapter; temp_dxgi_factory->EnumAdapters(adapter_index, adapter.GetAddressOf()); PFN_D3D11_CREATE_DEVICE d3d11_create_device; if (!temp_lib.Open("d3d11.dll") || !temp_lib.GetSymbol("D3D11CreateDevice", &d3d11_create_device)) { return false; } HRESULT hr = d3d11_create_device( adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, s_supported_feature_levels.data(), static_cast(s_supported_feature_levels.size()), D3D11_SDK_VERSION, temp_device.GetAddressOf(), nullptr, nullptr); if (FAILED(hr)) return false; if (FAILED(temp_device.As(&temp_device1))) return false; } if (!temp_device1) return false; D3D11_FEATURE_DATA_D3D11_OPTIONS options{}; if (FAILED(temp_device1->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)))) { return false; } return options.OutputMergerLogicOp != FALSE; } } // namespace D3D } // namespace DX11