dolphin/Source/Core/VideoBackends/D3D/D3DBase.cpp

282 lines
8.2 KiB
C++

// Copyright 2010 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoBackends/D3D/D3DBase.h"
#include <algorithm>
#include <array>
#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<IDXGIFactory> dxgi_factory;
ComPtr<ID3D11Device> device;
ComPtr<ID3D11Device1> device1;
ComPtr<ID3D11DeviceContext> context;
D3D_FEATURE_LEVEL feature_level;
static ComPtr<ID3D11Debug> s_debug;
constexpr std::array<D3D_FEATURE_LEVEL, 3> 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<IDXGIAdapter> 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<UINT>(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<ID3D11InfoQueue> 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<UINT>(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<StateManager>();
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<u32> GetAAModes(u32 adapter_index)
{
// Use temporary device if we don't have one already.
Common::DynamicLibrary temp_lib;
ComPtr<ID3D11Device> temp_device = device;
D3D_FEATURE_LEVEL temp_feature_level = feature_level;
if (!temp_device)
{
ComPtr<IDXGIFactory> temp_dxgi_factory = D3DCommon::CreateDXGIFactory(false);
if (!temp_dxgi_factory)
return {};
ComPtr<IDXGIAdapter> 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<UINT>(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<u32> 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<ID3D11Device1> temp_device1 = device1;
if (!device)
{
ComPtr<ID3D11Device> temp_device;
ComPtr<IDXGIFactory> temp_dxgi_factory = D3DCommon::CreateDXGIFactory(false);
if (!temp_dxgi_factory)
return false;
ComPtr<IDXGIAdapter> 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<UINT>(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