rtengine/src/renderer/dx11/device.cpp
2024-07-30 21:57:46 +02:00

196 lines
7.6 KiB
C++

#include "device.hpp"
#include "runtime/config.h"
static RT_CVAR_S(rt_Dx11AdapterName, "Name of the desired adapter (GPU). Default: Empty.", "");
rt_dx11_device::rt_dx11_device() : m_is_initialized(false) {
}
rt_dx11_device::~rt_dx11_device() {
}
rt_result rt_dx11_device::Initialize(const rt_renderer_window_info *info *info) {
// Create the necessary objects (Device, SwapChain, Immediate Context)
HRESULT hr = S_OK;
ComPtr<IDXGIFactory1> factory;
if (FAILED(hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) {
rtReportError("DX11", "CreateDXGIFactory1 failed with %d", hr);
return RT_UNKNOWN_ERROR;
}
if (FAILED(hr = factory.As(&m_dxgi_factory))) {
rtReportError("DX11", "Failed to retrieve IDXGIFactory4 with %d", hr);
return RT_UNKNOWN_ERROR;
}
// Retrieve the selected adapter
{
ComPtr<IDXGIAdapter1> adapter = nullptr;
WCHAR w_name[128];
memset(w_name, 0, sizeof(w_name));
if (rtUTF8ToWStr(rt_Dx11AdapterName.s, w_name, RT_ARRAY_COUNT(w_name)) != RT_SUCCESS) {
rtLog("DX11",
"The provided adapter name does not fit into the statically sized array.");
if (FAILED(m_dxgi_factory->EnumAdapters1(0, &adapter))) {
rtReportError("DX11", "Failed to retrieve the default adapter.");
return RT_UNKNOWN_ERROR;
}
if (FAILED(hr = adapter.As(&m_adapter))) {
rtReportError("DX11", "Failed to retrieve IDXGIAdapter3 with %d", hr);
return RT_UNKNOWN_ERROR;
}
}
UINT i = 0;
while (m_dxgi_factory->EnumAdapters1(i, &adapter) == S_OK) {
DXGI_ADAPTER_DESC1 desc;
if (FAILED(adapter->GetDesc1(&desc))) {
++i;
adapter->Release();
continue;
}
if (memcmp(desc.Description, w_name, sizeof(w_name)) == 0) {
if (FAILED(hr = adapter.As(&m_adapter))) {
rtReportError("DX11", "Failed to retrieve IDXGIAdapter3 with %d", hr);
return RT_UNKNOWN_ERROR;
}
break;
}
++i;
}
if (!m_adapter.Get()) {
if (FAILED(m_dxgi_factory->EnumAdapters1(0, &adapter))) {
rtReportError("DX11", "Failed to retrieve the default adapter.");
return RT_UNKNOWN_ERROR;
}
if (FAILED(hr = adapter.As(&m_adapter))) {
rtReportError("DX11", "Failed to retrieve IDXGIAdapter3 with %d", hr);
return RT_UNKNOWN_ERROR;
}
}
}
{
// Get monitor properties
ComPtr<IDXGIOutput> output;
HMONITOR wnd_mon = MonitorFromWindow(info->hWnd, MONITOR_DEFAULTTOPRIMARY);
if (wnd_mon) {
UINT i = 0;
while (m_adapter->EnumOutputs(i, &output) == S_OK) {
DXGI_OUTPUT_DESC desc = {};
output->GetDesc(&desc);
if (desc.Monitor == wnd_mon) {
GetDisplayInfo(output.Get());
break;
}
++i;
}
} else {
rtLog("DX11", "MonitorFromWindow returned a non-valid monitor.");
if (!FAILED(m_adapter->EnumOutputs(0, &output))) {
GetDisplayInfo(output.Get());
}
}
}
UINT device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef RT_DEBUG
device_flags |= D3D11_CREATE_DEVICE_DEBUGGABLE | D3D11_CREATE_DEVICE_DEBUG;
#endif
// Check for tearing support, which is a requirement for variable refresh displays
if (FAILED(m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
&m_tearing_supported,
sizeof(m_tearing_supported))))
m_tearing_supported = false;
D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1};
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
if (FAILED(hr = D3D11CreateDevice(m_adapter.Get(),
D3D_DRIVER_TYPE_HARDWARE,
NULL,
device_flags,
feature_levels,
RT_ARRAY_COUNT(feature_levels),
D3D11_SDK_VERSION,
&device,
nullptr,
&context))) {
rtLog("DX11", "D3D11CreateDevice failed with %d. Retrying without DEBUGGABLE flag...");
device_flags &= ~D3D11_CREATE_DEVICE_DEBUGGABLE;
if (FAILED(hr = D3D11CreateDevice(nullptr, // Use the default adapter TODO: Select
D3D_DRIVER_TYPE_HARDWARE,
NULL,
device_flags,
feature_levels,
RT_ARRAY_COUNT(feature_levels),
D3D11_SDK_VERSION,
&device,
nullptr,
&context))) {
rtReportError("DX11", "D3D11CreateDevice failed with %d", hr);
return RT_UNKNOWN_ERROR;
}
}
if (FAILED(hr = device.As(&m_device))) {
rtReportError("DX11", "Failed to retrieve ID3D11Device5 with %d", hr);
return RT_UNKNOWN_ERROR;
}
if (FAILED(hr = context.As(&m_context))) {
rtReportError("DX11", "Failed to retrieve ID3D11DeviceContext3 with %d", hr);
return RT_UNKNOWN_ERROR;
}
if (info->is_fullscreen) {
// Don't allow Alt+Enter to switch the window to fullscreen exclusive
m_dxgi_factory->MakeWindowAssociation(info->hWnd, DXGI_MWA_NO_ALT_ENTER);
}
UINT swap_chain_flags = 0;
if (m_tearing_supported)
swap_chain_flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
// TODO(Kevin): HDR Support
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = info->width;
swap_chain_desc.Height = info->height;
swap_chain_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swap_chain_desc.Stereo = FALSE;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.BufferCount = 2;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swap_chain_desc.Scaling = DXGI_SCALING_STRETCH;
swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
swap_chain_desc.Flags = swap_chain_flags;
if (FAILED(hr = m_dxgi_factory->CreateSwapChainForHwnd(m_device.Get(),
info->hWnd,
&swap_chain_desc,
nullptr,
nullptr,
&m_swap_chain))) {
rtReportError("DX11", "CreateSwapChainForHwnd failed with %d", hr);
return RT_UNKNOWN_ERROR;
}
m_is_initialized = true;
return RT_SUCCESS;
}
void rt_dx11_device::Shutdown(void) {
m_device.Reset();
m_is_initialized = false;
}
void rt_dx11_device::GetDisplayInfo(IDXGIOutput *output) {
// TODO Not implemented yet
m_monitor_refresh_rate = 1.f / 60.f;
}