// Copyright 2010 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include #include #include "Common/CommonTypes.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" #include "Common/Logging/Log.h" #include "VideoBackends/D3D12/D3DBase.h" #include "VideoBackends/D3D12/D3DCommandListManager.h" #include "VideoBackends/D3D12/D3DDescriptorHeapManager.h" #include "VideoBackends/D3D12/D3DState.h" #include "VideoBackends/D3D12/D3DTexture.h" #include "VideoCommon/VideoConfig.h" static const unsigned int SWAP_CHAIN_BUFFER_COUNT = 4; namespace DX12 { // d3dcompiler_*.dll exports static HINSTANCE s_d3d_compiler_dll = nullptr; static int s_d3d_compiler_dll_ref = 0; D3DREFLECT d3d_reflect = nullptr; D3DCREATEBLOB d3d_create_blob = nullptr; pD3DCompile d3d_compile = nullptr; // dxgi.dll exports static HINSTANCE s_dxgi_dll = nullptr; static int s_dxgi_dll_ref = 0; CREATEDXGIFACTORY create_dxgi_factory = nullptr; // d3d12.dll exports static HINSTANCE s_d3d12_dll = nullptr; static int s_d3d12_dll_ref = 0; D3D12CREATEDEVICE d3d12_create_device = nullptr; D3D12SERIALIZEROOTSIGNATURE d3d12_serialize_root_signature = nullptr; D3D12GETDEBUGINTERFACE d3d12_get_debug_interface = nullptr; namespace D3D { // Begin extern'd variables. ID3D12Device* device12 = nullptr; ID3D12CommandQueue* command_queue = nullptr; std::unique_ptr command_list_mgr; ID3D12GraphicsCommandList* current_command_list = nullptr; ID3D12RootSignature* default_root_signature = nullptr; D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu = {}; D3D12_CPU_DESCRIPTOR_HANDLE null_srv_cpu_shadow = {}; unsigned int resource_descriptor_size = 0; unsigned int sampler_descriptor_size = 0; std::unique_ptr gpu_descriptor_heap_mgr; std::unique_ptr sampler_descriptor_heap_mgr; std::unique_ptr dsv_descriptor_heap_mgr; std::unique_ptr rtv_descriptor_heap_mgr; std::array gpu_descriptor_heaps; HWND hWnd; // End extern'd variables. static IDXGISwapChain* s_swap_chain = nullptr; static unsigned int s_monitor_refresh_rate = 0; static LARGE_INTEGER s_qpc_frequency; static ID3D12DebugDevice* s_debug_device12 = nullptr; static D3D_FEATURE_LEVEL s_feat_level; static D3DTexture2D* s_backbuf[SWAP_CHAIN_BUFFER_COUNT]; static unsigned int s_current_back_buf = 0; static unsigned int s_xres = 0; static unsigned int s_yres = 0; static bool s_frame_in_progress = false; static std::vector s_aa_modes; // supported AA modes of the current adapter static const D3D_FEATURE_LEVEL s_supported_feature_levels[] = { D3D_FEATURE_LEVEL_11_0 }; HRESULT LoadDXGI() { if (s_dxgi_dll_ref++ > 0) return S_OK; if (s_dxgi_dll) return S_OK; s_dxgi_dll = LoadLibraryA("dxgi.dll"); if (!s_dxgi_dll) { MessageBoxA(nullptr, "Failed to load dxgi.dll", "Critical error", MB_OK | MB_ICONERROR); --s_dxgi_dll_ref; return E_FAIL; } create_dxgi_factory = (CREATEDXGIFACTORY)GetProcAddress(s_dxgi_dll, "CreateDXGIFactory"); if (create_dxgi_factory == nullptr) MessageBoxA(nullptr, "GetProcAddress failed for CreateDXGIFactory!", "Critical error", MB_OK | MB_ICONERROR); return S_OK; } HRESULT LoadD3D() { if (s_d3d12_dll_ref++ > 0) return S_OK; s_d3d12_dll = LoadLibraryA("d3d12.dll"); if (!s_d3d12_dll) { MessageBoxA(nullptr, "Failed to load d3d12.dll", "Critical error", MB_OK | MB_ICONERROR); --s_d3d12_dll_ref; return E_FAIL; } d3d12_create_device = (D3D12CREATEDEVICE)GetProcAddress(s_d3d12_dll, "D3D12CreateDevice"); if (d3d12_create_device == nullptr) { MessageBoxA(nullptr, "GetProcAddress failed for D3D12CreateDevice!", "Critical error", MB_OK | MB_ICONERROR); return E_FAIL; } d3d12_serialize_root_signature = (D3D12SERIALIZEROOTSIGNATURE)GetProcAddress(s_d3d12_dll, "D3D12SerializeRootSignature"); if (d3d12_serialize_root_signature == nullptr) { MessageBoxA(nullptr, "GetProcAddress failed for D3D12SerializeRootSignature!", "Critical error", MB_OK | MB_ICONERROR); return E_FAIL; } d3d12_get_debug_interface = (D3D12GETDEBUGINTERFACE)GetProcAddress(s_d3d12_dll, "D3D12GetDebugInterface"); if (d3d12_get_debug_interface == nullptr) { MessageBoxA(nullptr, "GetProcAddress failed for D3D12GetDebugInterface!", "Critical error", MB_OK | MB_ICONERROR); return E_FAIL; } return S_OK; } HRESULT LoadD3DCompiler() { if (s_d3d_compiler_dll_ref++ > 0) return S_OK; if (s_d3d_compiler_dll) return S_OK; // try to load D3DCompiler first to check whether we have proper runtime support // try to use the dll the backend was compiled against first - don't bother about debug runtimes s_d3d_compiler_dll = LoadLibraryA(D3DCOMPILER_DLL_A); if (!s_d3d_compiler_dll) { // if that fails, use the dll which should be available in every SDK which officially supports DX12. s_d3d_compiler_dll = LoadLibraryA("D3DCompiler_42.dll"); if (!s_d3d_compiler_dll) { MessageBoxA(nullptr, "Failed to load D3DCompiler_42.dll, update your DX12 runtime, please", "Critical error", MB_OK | MB_ICONERROR); return E_FAIL; } else { NOTICE_LOG(VIDEO, "Successfully loaded D3DCompiler_42.dll. If you're having trouble, try updating your DX runtime first."); } } d3d_reflect = (D3DREFLECT) GetProcAddress(s_d3d_compiler_dll, "D3DReflect"); if (d3d_reflect == nullptr) MessageBoxA(nullptr, "GetProcAddress failed for D3DReflect!", "Critical error", MB_OK | MB_ICONERROR); d3d_create_blob = (D3DCREATEBLOB)GetProcAddress(s_d3d_compiler_dll, "D3DCreateBlob"); if (d3d_create_blob == nullptr) MessageBoxA(nullptr, "GetProcAddress failed for D3DCreateBlob!", "Critical error", MB_OK | MB_ICONERROR); d3d_compile = (pD3DCompile) GetProcAddress(s_d3d_compiler_dll, "D3DCompile"); if (d3d_compile == nullptr) MessageBoxA(nullptr, "GetProcAddress failed for D3DCompile!", "Critical error", MB_OK | MB_ICONERROR); return S_OK; } void UnloadDXGI() { if (!s_dxgi_dll_ref) return; if (--s_dxgi_dll_ref != 0) return; if (s_dxgi_dll) FreeLibrary(s_dxgi_dll); s_dxgi_dll = nullptr; create_dxgi_factory = nullptr; } void UnloadD3D() { if (!s_d3d12_dll_ref) return; if (--s_d3d12_dll_ref != 0) return; if (s_d3d12_dll) FreeLibrary(s_d3d12_dll); s_d3d12_dll = nullptr; d3d12_create_device = nullptr; d3d12_serialize_root_signature = nullptr; } void UnloadD3DCompiler() { if (!s_d3d_compiler_dll_ref) return; if (--s_d3d_compiler_dll_ref != 0) return; if (s_d3d_compiler_dll) FreeLibrary(s_d3d_compiler_dll); s_d3d_compiler_dll = nullptr; d3d_compile = nullptr; d3d_create_blob = nullptr; d3d_reflect = nullptr; } bool AlertUserIfSelectedAdapterDoesNotSupportD3D12() { HRESULT hr = LoadDXGI(); if (SUCCEEDED(hr)) { hr = LoadD3D(); } if (FAILED(hr)) { // LoadDXGI / LoadD3D display a specific error message, // no need to do that here. return false; } IDXGIFactory* factory = nullptr; IDXGIAdapter* adapter = nullptr; ID3D12Device* device = nullptr; if (SUCCEEDED(hr)) { hr = create_dxgi_factory(__uuidof(IDXGIFactory), (void**)&factory); } if (SUCCEEDED(hr)) { hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter); } if (SUCCEEDED(hr)) { hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device)); SAFE_RELEASE(device); SAFE_RELEASE(adapter); SAFE_RELEASE(factory); if (FAILED(hr)) { UnloadD3D(); UnloadDXGI(); MessageBoxA(nullptr, "Failed to create a D3D12 device on the selected adapter.\n\nPlease make sure it supports Direct3D 12, and that your graphics drivers are up-to-date.", "Critical error", MB_OK | MB_ICONERROR); return false; } // If succeeded, leave DXGI and D3D libraries loaded since we'll use them in Create(). return true; } // DXGI failed to create factory/enumerate adapter. This should be very uncommon. MessageBoxA(nullptr, "Failed to create enumerate selected adapter. Please select a different graphics adapter.", "Critical error", MB_OK | MB_ICONERROR); SAFE_RELEASE(adapter); SAFE_RELEASE(factory); UnloadD3D(); UnloadDXGI(); return false; } std::vector EnumAAModes(IDXGIAdapter* adapter) { std::vector aa_modes; bool d3d12_supported = AlertUserIfSelectedAdapterDoesNotSupportD3D12(); if (!d3d12_supported) return aa_modes; ID3D12Device* device12 = nullptr; d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12)); if (device12) { for (int samples = 0; samples < D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT; ++samples) { D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS multisample_quality_levels = {}; multisample_quality_levels.Format = DXGI_FORMAT_R8G8B8A8_UNORM; multisample_quality_levels.SampleCount = samples; device12->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &multisample_quality_levels, sizeof(multisample_quality_levels)); DXGI_SAMPLE_DESC desc; desc.Count = samples; desc.Quality = 0; if (multisample_quality_levels.NumQualityLevels > 0) { aa_modes.push_back(desc); } } device12->Release(); } return aa_modes; } D3D_FEATURE_LEVEL GetFeatureLevel(IDXGIAdapter* adapter) { return D3D_FEATURE_LEVEL_11_0; } HRESULT Create(HWND wnd) { hWnd = wnd; HRESULT hr; RECT client; GetClientRect(hWnd, &client); s_xres = client.right - client.left; s_yres = client.bottom - client.top; hr = LoadDXGI(); if (SUCCEEDED(hr)) hr = LoadD3D(); if (SUCCEEDED(hr)) hr = LoadD3DCompiler(); if (FAILED(hr)) { UnloadDXGI(); UnloadD3D(); UnloadD3DCompiler(); return hr; } IDXGIFactory* factory; IDXGIAdapter* adapter; hr = create_dxgi_factory(__uuidof(IDXGIFactory), (void**)&factory); if (FAILED(hr)) MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR); hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter); if (FAILED(hr)) { // try using the first one hr = factory->EnumAdapters(0, &adapter); if (FAILED(hr)) MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR); } // get supported AA modes s_aa_modes = EnumAAModes(adapter); if (std::find_if( s_aa_modes.begin(), s_aa_modes.end(), [](const DXGI_SAMPLE_DESC& desc) {return desc.Count == g_Config.iMultisamples; } ) == s_aa_modes.end()) { g_Config.iMultisamples = 1; UpdateActiveConfig(); } DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; swap_chain_desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc.OutputWindow = wnd; swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.SampleDesc.Quality = 0; swap_chain_desc.Windowed = true; swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; swap_chain_desc.Flags = 0; swap_chain_desc.BufferDesc.Width = s_xres; swap_chain_desc.BufferDesc.Height = s_yres; swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; #if defined(_DEBUG) || defined(DEBUGFAST) || defined(USE_D3D12_DEBUG_LAYER) // Enabling the debug layer will fail if the Graphics Tools feature is not installed. if (SUCCEEDED(hr)) { ID3D12Debug* debug_controller; hr = d3d12_get_debug_interface(IID_PPV_ARGS(&debug_controller)); if (SUCCEEDED(hr)) { debug_controller->EnableDebugLayer(); debug_controller->Release(); } else { MessageBox(wnd, _T("WARNING: Failed to enable D3D12 debug layer, please ensure the Graphics Tools feature is installed."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR); } } #endif if (SUCCEEDED(hr)) { hr = d3d12_create_device(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device12)); s_feat_level = D3D_FEATURE_LEVEL_11_0; } if (SUCCEEDED(hr)) { D3D12_COMMAND_QUEUE_DESC command_queue_desc = { D3D12_COMMAND_LIST_TYPE_DIRECT, // D3D12_COMMAND_LIST_TYPE Type; 0, // INT Priority; D3D12_COMMAND_QUEUE_FLAG_NONE, // D3D12_COMMAND_QUEUE_FLAG Flags; 0 // UINT NodeMask; }; CheckHR(device12->CreateCommandQueue(&command_queue_desc, IID_PPV_ARGS(&command_queue))); IDXGIFactory* factory = nullptr; adapter->GetParent(IID_PPV_ARGS(&factory)); CheckHR(factory->CreateSwapChain(command_queue, &swap_chain_desc, &s_swap_chain)); s_current_back_buf = 0; factory->Release(); } if (SUCCEEDED(hr)) { // Query the monitor refresh rate, to ensure proper Present throttling behavior. DEVMODE dev_mode; memset(&dev_mode, 0, sizeof(DEVMODE)); dev_mode.dmSize = sizeof(DEVMODE); dev_mode.dmDriverExtra = 0; if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode) == 0) { // If EnumDisplaySettings fails, assume monitor refresh rate of 60 Hz. s_monitor_refresh_rate = 60; } else { s_monitor_refresh_rate = dev_mode.dmDisplayFrequency; } } if (FAILED(hr)) { MessageBox(wnd, _T("Failed to initialize Direct3D.\nMake sure your video card supports Direct3D 12 and your drivers are up-to-date."), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR); SAFE_RELEASE(s_swap_chain); return E_FAIL; } ID3D12InfoQueue* info_queue = nullptr; if (SUCCEEDED(device12->QueryInterface(&info_queue))) { CheckHR(info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, TRUE)); CheckHR(info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, TRUE)); D3D12_INFO_QUEUE_FILTER filter = {}; D3D12_MESSAGE_ID id_list[] = { D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_DEPTHSTENCILVIEW_NOT_SET, // Benign. D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, // Benign. D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_TYPE_MISMATCH, // Benign. D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, // Benign. }; filter.DenyList.NumIDs = ARRAYSIZE(id_list); filter.DenyList.pIDList = id_list; info_queue->PushStorageFilter(&filter); info_queue->Release(); // Used at Close time to report live objects. CheckHR(device12->QueryInterface(&s_debug_device12)); } // prevent DXGI from responding to Alt+Enter, unfortunately DXGI_MWA_NO_ALT_ENTER // does not work so we disable all monitoring of window messages. However this // may make it more difficult for DXGI to handle display mode changes. hr = factory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES); if (FAILED(hr)) MessageBox(wnd, _T("Failed to associate the window"), _T("Dolphin Direct3D 12 backend"), MB_OK | MB_ICONERROR); SAFE_RELEASE(factory); SAFE_RELEASE(adapter) CreateDescriptorHeaps(); CreateRootSignatures(); command_list_mgr = std::make_unique( D3D12_COMMAND_LIST_TYPE_DIRECT, device12, command_queue ); command_list_mgr->GetCommandList(¤t_command_list); command_list_mgr->SetInitialCommandListState(); for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++) { ID3D12Resource* buf12 = nullptr; hr = s_swap_chain->GetBuffer(i, IID_PPV_ARGS(&buf12)); CHECK(SUCCEEDED(hr), "Retrieve back buffer texture"); s_backbuf[i] = new D3DTexture2D(buf12, D3D11_BIND_RENDER_TARGET, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, false, D3D12_RESOURCE_STATE_PRESENT // Swap Chain back buffers start out in D3D12_RESOURCE_STATE_PRESENT. ); SAFE_RELEASE(buf12); SetDebugObjectName12(s_backbuf[i]->GetTex12(), "backbuffer texture"); } s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); current_command_list->OMSetRenderTargets(1, &s_backbuf[s_current_back_buf]->GetRTV12(), FALSE, nullptr); QueryPerformanceFrequency(&s_qpc_frequency); return S_OK; } void CreateDescriptorHeaps() { // Create D3D12 GPU and CPU descriptor heaps. { D3D12_DESCRIPTOR_HEAP_DESC gpu_descriptor_heap_desc = {}; gpu_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; gpu_descriptor_heap_desc.NumDescriptors = 500000; gpu_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; gpu_descriptor_heap_mgr = std::make_unique(&gpu_descriptor_heap_desc, device12, 50000); gpu_descriptor_heaps[0] = gpu_descriptor_heap_mgr->GetDescriptorHeap(); D3D12_CPU_DESCRIPTOR_HANDLE descriptor_heap_cpu_base = gpu_descriptor_heap_mgr->GetDescriptorHeap()->GetCPUDescriptorHandleForHeapStart(); resource_descriptor_size = device12->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); sampler_descriptor_size = device12->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); D3D12_GPU_DESCRIPTOR_HANDLE null_srv_gpu = {}; gpu_descriptor_heap_mgr->Allocate(&null_srv_cpu, &null_srv_gpu, &null_srv_cpu_shadow); D3D12_SHADER_RESOURCE_VIEW_DESC null_srv_desc = {}; null_srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; null_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; null_srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; device12->CreateShaderResourceView(NULL, &null_srv_desc, null_srv_cpu); for (UINT i = 0; i < 500000; i++) { // D3D12TODO: Make paving of descriptor heap optional. D3D12_CPU_DESCRIPTOR_HANDLE destination_descriptor = {}; destination_descriptor.ptr = descriptor_heap_cpu_base.ptr + i * resource_descriptor_size; device12->CreateShaderResourceView(NULL, &null_srv_desc, destination_descriptor); } } { D3D12_DESCRIPTOR_HEAP_DESC sampler_descriptor_heap_desc = {}; sampler_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; sampler_descriptor_heap_desc.NumDescriptors = 2000; sampler_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; sampler_descriptor_heap_mgr = std::make_unique(&sampler_descriptor_heap_desc, device12); gpu_descriptor_heaps[1] = sampler_descriptor_heap_mgr->GetDescriptorHeap(); } { D3D12_DESCRIPTOR_HEAP_DESC dsv_descriptor_heap_desc = {}; dsv_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; dsv_descriptor_heap_desc.NumDescriptors = 2000; dsv_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; dsv_descriptor_heap_mgr = std::make_unique(&dsv_descriptor_heap_desc, device12); } { // D3D12TODO: Temporary workaround.. really need to properly suballocate out of render target heap. D3D12_DESCRIPTOR_HEAP_DESC rtv_descriptor_heap_desc = {}; rtv_descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; rtv_descriptor_heap_desc.NumDescriptors = 1000000; rtv_descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; rtv_descriptor_heap_mgr = std::make_unique(&rtv_descriptor_heap_desc, device12); } } void CreateRootSignatures() { D3D12_DESCRIPTOR_RANGE desc_range_srv = { D3D12_DESCRIPTOR_RANGE_TYPE_SRV, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType; 8, // UINT NumDescriptors; 0, // UINT BaseShaderRegister; 0, // UINT RegisterSpace; D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart; }; D3D12_DESCRIPTOR_RANGE desc_range_sampler = { D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType; 8, // UINT NumDescriptors; 0, // UINT BaseShaderRegister; 0, // UINT RegisterSpace; D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart; }; D3D12_DESCRIPTOR_RANGE desc_range_uav = { D3D12_DESCRIPTOR_RANGE_TYPE_UAV, // D3D12_DESCRIPTOR_RANGE_TYPE RangeType; 1, // UINT NumDescriptors; 2, // UINT BaseShaderRegister; 0, // UINT RegisterSpace; D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND // UINT OffsetInDescriptorsFromTableStart; }; D3D12_ROOT_PARAMETER root_parameters[NUM_GRAPHICS_ROOT_PARAMETERS]; root_parameters[DESCRIPTOR_TABLE_PS_SRV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; root_parameters[DESCRIPTOR_TABLE_PS_SRV].DescriptorTable.NumDescriptorRanges = 1; root_parameters[DESCRIPTOR_TABLE_PS_SRV].DescriptorTable.pDescriptorRanges = &desc_range_srv; root_parameters[DESCRIPTOR_TABLE_PS_SRV].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].DescriptorTable.NumDescriptorRanges = 1; root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].DescriptorTable.pDescriptorRanges = &desc_range_sampler; root_parameters[DESCRIPTOR_TABLE_PS_SAMPLER].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; root_parameters[DESCRIPTOR_TABLE_GS_CBV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; root_parameters[DESCRIPTOR_TABLE_GS_CBV].Descriptor.RegisterSpace = 0; root_parameters[DESCRIPTOR_TABLE_GS_CBV].Descriptor.ShaderRegister = 0; root_parameters[DESCRIPTOR_TABLE_GS_CBV].ShaderVisibility = D3D12_SHADER_VISIBILITY_GEOMETRY; root_parameters[DESCRIPTOR_TABLE_VS_CBV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; root_parameters[DESCRIPTOR_TABLE_VS_CBV].Descriptor.RegisterSpace = 0; root_parameters[DESCRIPTOR_TABLE_VS_CBV].Descriptor.ShaderRegister = 0; root_parameters[DESCRIPTOR_TABLE_VS_CBV].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].Descriptor.RegisterSpace = 0; root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].Descriptor.ShaderRegister = 0; root_parameters[DESCRIPTOR_TABLE_PS_CBVONE].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].Descriptor.RegisterSpace = 0; root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].Descriptor.ShaderRegister = 1; root_parameters[DESCRIPTOR_TABLE_PS_CBVTWO].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; root_parameters[DESCRIPTOR_TABLE_PS_UAV].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; root_parameters[DESCRIPTOR_TABLE_PS_UAV].DescriptorTable.NumDescriptorRanges = 1; root_parameters[DESCRIPTOR_TABLE_PS_UAV].DescriptorTable.pDescriptorRanges = &desc_range_uav; root_parameters[DESCRIPTOR_TABLE_PS_UAV].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; D3D12_ROOT_SIGNATURE_DESC root_signature_desc = {}; root_signature_desc.pParameters = root_parameters; root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS; root_signature_desc.NumParameters = ARRAYSIZE(root_parameters); ID3DBlob* text_root_signature_blob; ID3DBlob* text_root_signature_error_blob; CheckHR(d3d12_serialize_root_signature(&root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, &text_root_signature_blob, &text_root_signature_error_blob)); CheckHR(D3D::device12->CreateRootSignature(0, text_root_signature_blob->GetBufferPointer(), text_root_signature_blob->GetBufferSize(), IID_PPV_ARGS(&default_root_signature))); } void WaitForOutstandingRenderingToComplete() { command_list_mgr->ExecuteQueuedWork(true); } void Close() { // we can't release the swapchain while in fullscreen. s_swap_chain->SetFullscreenState(false, nullptr); // Release all back buffer references for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++) { SAFE_RELEASE(s_backbuf[i]); } D3D::CleanupPersistentD3DTextureResources(); SAFE_RELEASE(s_swap_chain); command_list_mgr.reset(); command_queue->Release(); default_root_signature->Release(); gpu_descriptor_heap_mgr.reset(); sampler_descriptor_heap_mgr.reset(); rtv_descriptor_heap_mgr.reset(); dsv_descriptor_heap_mgr.reset(); ULONG remaining_references = device12->Release(); if ((!s_debug_device12 && remaining_references) || (s_debug_device12 && remaining_references > 1)) { ERROR_LOG(VIDEO, "Unreleased D3D12 references: %i.", remaining_references); } else { NOTICE_LOG(VIDEO, "Successfully released all D3D12 device references!"); } #if defined(_DEBUG) || defined(DEBUGFAST) if (s_debug_device12) { --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_device12->ReportLiveDeviceObjects(D3D12_RLDO_DETAIL); } SAFE_RELEASE(s_debug_device12); } #endif device12 = nullptr; current_command_list = nullptr; // unload DLLs UnloadDXGI(); UnloadD3DCompiler(); UnloadD3D(); } const std::string VertexShaderVersionString() { return "vs_5_0"; } const std::string GeometryShaderVersionString() { return "gs_5_0"; } const std::string PixelShaderVersionString() { return "ps_5_0"; } D3DTexture2D* &GetBackBuffer() { return s_backbuf[s_current_back_buf]; } unsigned int GetBackBufferWidth() { return s_xres; } unsigned int GetBackBufferHeight() { return s_yres; } // Returns the maximum width/height of a texture. unsigned int GetMaxTextureSize() { return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; } void Reset() { // release all back buffer references for (UINT i = 0; i < ARRAYSIZE(s_backbuf); i++) { SAFE_RELEASE(s_backbuf[i]); } // Block until all commands have finished. // This will also final-release all pending resources (including the backbuffer above) command_list_mgr->ExecuteQueuedWork(true); // resize swapchain buffers RECT client; GetClientRect(hWnd, &client); s_xres = client.right - client.left; s_yres = client.bottom - client.top; CheckHR(s_swap_chain->ResizeBuffers(SWAP_CHAIN_BUFFER_COUNT, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM, 0)); // recreate back buffer textures HRESULT hr = S_OK; for (UINT i = 0; i < SWAP_CHAIN_BUFFER_COUNT; i++) { ID3D12Resource* buf12 = nullptr; hr = s_swap_chain->GetBuffer(i, IID_PPV_ARGS(&buf12)); CHECK(SUCCEEDED(hr), "Retrieve back buffer texture"); s_backbuf[i] = new D3DTexture2D(buf12, D3D11_BIND_RENDER_TARGET, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, false, D3D12_RESOURCE_STATE_PRESENT ); SAFE_RELEASE(buf12); SetDebugObjectName12(s_backbuf[i]->GetTex12(), "backbuffer texture"); } // The 'about-to-be-presented' back buffer index is always set back to '0' upon ResizeBuffers, just like // creating a new swap chain. s_current_back_buf = 0; s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); } bool BeginFrame() { if (s_frame_in_progress) { PanicAlert("BeginFrame called although a frame is already in progress"); return false; } s_frame_in_progress = true; return (device12 != nullptr); } void EndFrame() { if (!s_frame_in_progress) { PanicAlert("EndFrame called although no frame is in progress"); return; } s_frame_in_progress = false; } void Present() { // The Present function contains logic to ensure we never Present faster than Windows can // send to the monitor. If we Present too fast, the Present call will start to block, and we'll be // throttled - obviously not desired if vsync is disabled and the emulated CPU speed is > 100%. // The throttling logic ensures that we don't Present more than twice in a given monitor vsync. // This is accomplished through timing data - there is a programmatic way to determine if a // Present call will block, however after investigation that is not feasible here (without invasive // workarounds), due to the fact this method does not actually call Present - we just queue a Present // command for the background thread to dispatch. // The monitor refresh rate is determined in Create(). static LARGE_INTEGER s_last_present_qpc; LARGE_INTEGER current_qpc; QueryPerformanceCounter(¤t_qpc); const double time_elapsed_since_last_present = static_cast(current_qpc.QuadPart - s_last_present_qpc.QuadPart) / s_qpc_frequency.QuadPart; unsigned int present_flags = 0; if (g_ActiveConfig.IsVSync() == false && time_elapsed_since_last_present < (1.0 / static_cast(s_monitor_refresh_rate)) / 2.0 ) { present_flags = DXGI_PRESENT_TEST; // Causes Present to be a no-op. } else { s_last_present_qpc = current_qpc; s_backbuf[s_current_back_buf]->TransitionToResourceState(current_command_list, D3D12_RESOURCE_STATE_PRESENT); s_current_back_buf = (s_current_back_buf + 1) % SWAP_CHAIN_BUFFER_COUNT; } command_list_mgr->ExecuteQueuedWorkAndPresent(s_swap_chain, g_ActiveConfig.IsVSync() ? 1 : 0, present_flags); command_list_mgr->m_cpu_access_last_frame = command_list_mgr->m_cpu_access_this_frame; command_list_mgr->m_cpu_access_this_frame = false; command_list_mgr->m_draws_since_last_execution = 0; } HRESULT SetFullscreenState(bool enable_fullscreen) { return S_OK; } HRESULT GetFullscreenState(bool* fullscreen_state) { // Fullscreen exclusive intentionally not supported in DX12 backend. No performance // difference between it and windowed full-screen due to usage of a FLIP swap chain. *fullscreen_state = false; return S_OK; } } // namespace D3D } // namespace DX12