/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2018 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/ui/d3d12/d3d12_provider.h" #include #include #include #include "xenia/base/logging.h" #include "xenia/ui/d3d12/d3d12_context.h" DEFINE_bool(d3d12_debug, false, "Enable Direct3D 12 and DXGI debug layer."); DEFINE_int32(d3d12_adapter, -1, "Index of the DXGI adapter to use. " "-1 for any physical adapter, -2 for WARP software rendering."); namespace xe { namespace ui { namespace d3d12 { std::unique_ptr D3D12Provider::Create(Window* main_window) { std::unique_ptr provider(new D3D12Provider(main_window)); InitializationResult result = provider->Initialize(); if (result != InitializationResult::kSucceeded) { if (result != InitializationResult::kLibraryLoadFailed) { xe::FatalError( "Unable to initialize Direct3D 12 graphics subsystem.\n" "\n" "Ensure that you have the latest drivers for your GPU and it " "supports Direct3D 12 feature level 11_0.\n" "\n" "See https://xenia.jp/faq/ for more information and a list of " "supported GPUs."); } return nullptr; } return provider; } D3D12Provider::D3D12Provider(Window* main_window) : GraphicsProvider(main_window) {} D3D12Provider::~D3D12Provider() { if (graphics_analysis_ != nullptr) { graphics_analysis_->Release(); } if (direct_queue_ != nullptr) { direct_queue_->Release(); } if (device_ != nullptr) { device_->Release(); } if (dxgi_factory_ != nullptr) { dxgi_factory_->Release(); } if (library_d3dcompiler_ != nullptr) { FreeLibrary(library_d3dcompiler_); } if (library_d3d12_ != nullptr) { FreeLibrary(library_d3d12_); } if (library_dxgi_ != nullptr) { FreeLibrary(library_dxgi_); } } D3D12Provider::InitializationResult D3D12Provider::Initialize() { // Load the libraries. library_dxgi_ = LoadLibrary(L"dxgi.dll"); library_d3d12_ = LoadLibrary(L"D3D12.dll"); library_d3dcompiler_ = LoadLibrary(L"D3DCompiler_47.dll"); if (library_dxgi_ == nullptr || library_d3d12_ == nullptr || library_d3dcompiler_ == nullptr) { return InitializationResult::kLibraryLoadFailed; } bool libraries_loaded = true; libraries_loaded &= (pfn_create_dxgi_factory2_ = PFNCreateDXGIFactory2( GetProcAddress(library_dxgi_, "CreateDXGIFactory2"))) != nullptr; libraries_loaded &= (pfn_dxgi_get_debug_interface1_ = PFNDXGIGetDebugInterface1( GetProcAddress(library_dxgi_, "DXGIGetDebugInterface1"))) != nullptr; libraries_loaded &= (pfn_d3d12_get_debug_interface_ = PFN_D3D12_GET_DEBUG_INTERFACE( GetProcAddress(library_d3d12_, "D3D12GetDebugInterface"))) != nullptr; libraries_loaded &= (pfn_d3d12_create_device_ = PFN_D3D12_CREATE_DEVICE( GetProcAddress(library_d3d12_, "D3D12CreateDevice"))) != nullptr; libraries_loaded &= (pfn_d3d12_serialize_root_signature_ = PFN_D3D12_SERIALIZE_ROOT_SIGNATURE( GetProcAddress(library_d3d12_, "D3D12SerializeRootSignature"))) != nullptr; libraries_loaded &= (pfn_d3d_disassemble_ = pD3DDisassemble(GetProcAddress( library_d3dcompiler_, "D3DDisassemble"))) != nullptr; if (!libraries_loaded) { return InitializationResult::kLibraryLoadFailed; } // Enable the debug layer. bool debug = FLAGS_d3d12_debug; if (debug) { ID3D12Debug* debug_interface; if (SUCCEEDED( pfn_d3d12_get_debug_interface_(IID_PPV_ARGS(&debug_interface)))) { debug_interface->EnableDebugLayer(); debug_interface->Release(); } else { XELOGW("Failed to enable the Direct3D 12 debug layer"); debug = false; } } // Create the DXGI factory. IDXGIFactory2* dxgi_factory; if (FAILED(pfn_create_dxgi_factory2_(debug ? DXGI_CREATE_FACTORY_DEBUG : 0, IID_PPV_ARGS(&dxgi_factory)))) { XELOGE("Failed to create a DXGI factory"); return InitializationResult::kDeviceInitializationFailed; } // Choose the adapter. uint32_t adapter_index = 0; IDXGIAdapter1* adapter = nullptr; while (dxgi_factory->EnumAdapters1(adapter_index, &adapter) == S_OK) { DXGI_ADAPTER_DESC1 adapter_desc; if (SUCCEEDED(adapter->GetDesc1(&adapter_desc))) { if (SUCCEEDED(pfn_d3d12_create_device_(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { if (FLAGS_d3d12_adapter >= 0) { if (adapter_index == FLAGS_d3d12_adapter) { break; } } else if (FLAGS_d3d12_adapter == -2) { if (adapter_desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { break; } } else { if (!(adapter_desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) { break; } } } } adapter->Release(); adapter = nullptr; ++adapter_index; } if (adapter == nullptr) { XELOGE("Failed to get an adapter supporting Direct3D feature level 11_0."); dxgi_factory->Release(); return InitializationResult::kDeviceInitializationFailed; } DXGI_ADAPTER_DESC adapter_desc; if (FAILED(adapter->GetDesc(&adapter_desc))) { XELOGE("Failed to get the DXGI adapter description."); adapter->Release(); dxgi_factory->Release(); return InitializationResult::kDeviceInitializationFailed; } adapter_vendor_id_ = adapter_desc.VendorId; size_t adapter_name_length = std::wcstombs(nullptr, adapter_desc.Description, 0); char* adapter_name_mbcs = reinterpret_cast(alloca((adapter_name_length + 1) * sizeof(char))); std::wcstombs(adapter_name_mbcs, adapter_desc.Description, adapter_name_length + 1); XELOGD3D("DXGI adapter: %s (vendor %.4X, device %.4X)", adapter_name_mbcs, adapter_desc.VendorId, adapter_desc.DeviceId); // Create the Direct3D 12 device. ID3D12Device* device; if (FAILED(pfn_d3d12_create_device_(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)))) { XELOGE("Failed to create a Direct3D 12 feature level 11_0 device."); adapter->Release(); dxgi_factory->Release(); return InitializationResult::kDeviceInitializationFailed; } adapter->Release(); // Create the command queue for graphics. D3D12_COMMAND_QUEUE_DESC queue_desc; queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queue_desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queue_desc.NodeMask = 0; ID3D12CommandQueue* direct_queue; if (FAILED(device->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&direct_queue)))) { XELOGE("Failed to create a direct command queue"); device->Release(); dxgi_factory->Release(); } dxgi_factory_ = dxgi_factory; device_ = device; direct_queue_ = direct_queue; // Get descriptor sizes for each type. descriptor_size_view_ = device->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); descriptor_size_sampler_ = device->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); descriptor_size_rtv_ = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); descriptor_size_dsv_ = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV); // Check if optional features are supported. rasterizer_ordered_views_supported_ = false; tiled_resources_tier_ = 0; D3D12_FEATURE_DATA_D3D12_OPTIONS options; if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)))) { rasterizer_ordered_views_supported_ = options.ROVsSupported ? true : false; tiled_resources_tier_ = uint32_t(options.TiledResourcesTier); } programmable_sample_positions_tier_ = 0; D3D12_FEATURE_DATA_D3D12_OPTIONS2 options2; if (SUCCEEDED(device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS2, &options2, sizeof(options2)))) { programmable_sample_positions_tier_ = uint32_t(options2.ProgrammableSamplePositionsTier); } virtual_address_bits_per_resource_ = 0; D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT virtual_address_support; if (SUCCEEDED(device->CheckFeatureSupport( D3D12_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT, &virtual_address_support, sizeof(virtual_address_support)))) { virtual_address_bits_per_resource_ = virtual_address_support.MaxGPUVirtualAddressBitsPerResource; } XELOGD3D("Direct3D 12 device features:"); XELOGD3D("* Max GPU virtual address bits per resource: %u", virtual_address_bits_per_resource_); XELOGD3D("* Programmable sample positions: tier %u", programmable_sample_positions_tier_); XELOGD3D("* Rasterizer-ordered views: %s", rasterizer_ordered_views_supported_ ? "yes" : "no"); XELOGD3D("* Tiled resources: tier %u", tiled_resources_tier_); // Get the graphics analysis interface, will silently fail if PIX is not // attached. pfn_dxgi_get_debug_interface1_(0, IID_PPV_ARGS(&graphics_analysis_)); return InitializationResult::kSucceeded; } std::unique_ptr D3D12Provider::CreateContext( Window* target_window) { auto new_context = std::unique_ptr(new D3D12Context(this, target_window)); if (!new_context->Initialize()) { return nullptr; } return std::unique_ptr(new_context.release()); } std::unique_ptr D3D12Provider::CreateOffscreenContext() { auto new_context = std::unique_ptr(new D3D12Context(this, nullptr)); if (!new_context->Initialize()) { return nullptr; } return std::unique_ptr(new_context.release()); } } // namespace d3d12 } // namespace ui } // namespace xe