diff --git a/Source/Core/VideoBackends/CMakeLists.txt b/Source/Core/VideoBackends/CMakeLists.txt index e670dd7d4a..b53d85e10a 100644 --- a/Source/Core/VideoBackends/CMakeLists.txt +++ b/Source/Core/VideoBackends/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(Software) add_subdirectory(Vulkan) if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_subdirectory(D3DCommon) add_subdirectory(D3D) endif() diff --git a/Source/Core/VideoBackends/D3DCommon/CMakeLists.txt b/Source/Core/VideoBackends/D3DCommon/CMakeLists.txt new file mode 100644 index 0000000000..ba24ba133f --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(videod3dcommon + Common.cpp + Common.h + Shader.cpp + Shader.h + SwapChain.cpp + SwapChain.h +) + +target_link_libraries(videod3dcommon +PUBLIC + common + videocommon + videod3dcommon +) diff --git a/Source/Core/VideoBackends/D3DCommon/Common.cpp b/Source/Core/VideoBackends/D3DCommon/Common.cpp new file mode 100644 index 0000000000..f82c2d3f8c --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/Common.cpp @@ -0,0 +1,320 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "Common/Assert.h" +#include "Common/DynamicLibrary.h" +#include "Common/MsgHandler.h" +#include "Common/StringUtil.h" +#include "VideoBackends/D3DCommon/Common.h" +#include "VideoCommon/TextureConfig.h" +#include "VideoCommon/VideoConfig.h" + +namespace D3DCommon +{ +pD3DCompile d3d_compile; + +static Common::DynamicLibrary s_dxgi_library; +static Common::DynamicLibrary s_d3dcompiler_library; +static bool s_libraries_loaded = false; + +static HRESULT (*create_dxgi_factory)(REFIID riid, _COM_Outptr_ void** ppFactory); +static HRESULT (*create_dxgi_factory2)(UINT Flags, REFIID riid, void** ppFactory); + +bool LoadLibraries() +{ + if (s_libraries_loaded) + return true; + + if (!s_dxgi_library.Open("dxgi.dll")) + { + PanicAlertT("Failed to load dxgi.dll"); + return false; + } + + if (!s_d3dcompiler_library.Open(D3DCOMPILER_DLL_A)) + { + PanicAlertT("Failed to load %s. If you are using Windows 7, try installing the " + "KB4019990 update package.", + D3DCOMPILER_DLL_A); + s_dxgi_library.Close(); + return false; + } + + // Required symbols. + if (!s_d3dcompiler_library.GetSymbol("D3DCompile", &d3d_compile) || + !s_dxgi_library.GetSymbol("CreateDXGIFactory", &create_dxgi_factory)) + { + PanicAlertT("Failed to find one or more D3D symbols"); + s_d3dcompiler_library.Close(); + s_dxgi_library.Close(); + return false; + } + + // Optional symbols. + s_dxgi_library.GetSymbol("CreateDXGIFactory2", &create_dxgi_factory2); + s_libraries_loaded = true; + return true; +} + +void UnloadLibraries() +{ + create_dxgi_factory = nullptr; + create_dxgi_factory2 = nullptr; + d3d_compile = nullptr; + s_d3dcompiler_library.Close(); + s_dxgi_library.Close(); + s_libraries_loaded = false; +} + +IDXGIFactory2* CreateDXGIFactory(bool debug_device) +{ + IDXGIFactory2* factory; + + // Use Win8.1 version if available. + if (create_dxgi_factory2 && + SUCCEEDED(create_dxgi_factory2(debug_device ? DXGI_CREATE_FACTORY_DEBUG : 0, + IID_PPV_ARGS(&factory)))) + { + return factory; + } + + // Fallback to original version, without debug support. + HRESULT hr = create_dxgi_factory(IID_PPV_ARGS(&factory)); + if (FAILED(hr)) + { + PanicAlert("CreateDXGIFactory() failed with HRESULT %08X", hr); + return nullptr; + } + + return factory; +} + +std::vector GetAdapterNames() +{ + Microsoft::WRL::ComPtr factory; + HRESULT hr = create_dxgi_factory(IID_PPV_ARGS(&factory)); + if (!SUCCEEDED(hr)) + return {}; + + std::vector adapters; + IDXGIAdapter* adapter; + while (factory->EnumAdapters(static_cast(adapters.size()), &adapter) != + DXGI_ERROR_NOT_FOUND) + { + std::string name; + DXGI_ADAPTER_DESC desc; + if (SUCCEEDED(adapter->GetDesc(&desc))) + name = UTF16ToUTF8(desc.Description); + + adapters.push_back(std::move(name)); + } + + return adapters; +} + +DXGI_FORMAT GetDXGIFormatForAbstractFormat(AbstractTextureFormat format, bool typeless) +{ + switch (format) + { + case AbstractTextureFormat::DXT1: + return DXGI_FORMAT_BC1_UNORM; + case AbstractTextureFormat::DXT3: + return DXGI_FORMAT_BC2_UNORM; + case AbstractTextureFormat::DXT5: + return DXGI_FORMAT_BC3_UNORM; + case AbstractTextureFormat::BPTC: + return DXGI_FORMAT_BC7_UNORM; + case AbstractTextureFormat::RGBA8: + return typeless ? DXGI_FORMAT_R8G8B8A8_TYPELESS : DXGI_FORMAT_R8G8B8A8_UNORM; + case AbstractTextureFormat::BGRA8: + return typeless ? DXGI_FORMAT_B8G8R8A8_TYPELESS : DXGI_FORMAT_B8G8R8A8_UNORM; + case AbstractTextureFormat::R16: + return typeless ? DXGI_FORMAT_R16_TYPELESS : DXGI_FORMAT_R16_UNORM; + case AbstractTextureFormat::R32F: + return typeless ? DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_R32_FLOAT; + case AbstractTextureFormat::D16: + return DXGI_FORMAT_R16_TYPELESS; + case AbstractTextureFormat::D24_S8: + return DXGI_FORMAT_R24G8_TYPELESS; + case AbstractTextureFormat::D32F: + return DXGI_FORMAT_R32_TYPELESS; + case AbstractTextureFormat::D32F_S8: + return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; + default: + PanicAlert("Unhandled texture format."); + return DXGI_FORMAT_R8G8B8A8_UNORM; + } +} +DXGI_FORMAT GetSRVFormatForAbstractFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::DXT1: + return DXGI_FORMAT_BC1_UNORM; + case AbstractTextureFormat::DXT3: + return DXGI_FORMAT_BC2_UNORM; + case AbstractTextureFormat::DXT5: + return DXGI_FORMAT_BC3_UNORM; + case AbstractTextureFormat::BPTC: + return DXGI_FORMAT_BC7_UNORM; + case AbstractTextureFormat::RGBA8: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case AbstractTextureFormat::BGRA8: + return DXGI_FORMAT_B8G8R8A8_UNORM; + case AbstractTextureFormat::R16: + return DXGI_FORMAT_R16_UNORM; + case AbstractTextureFormat::R32F: + return DXGI_FORMAT_R32_FLOAT; + case AbstractTextureFormat::D16: + return DXGI_FORMAT_R16_UNORM; + case AbstractTextureFormat::D24_S8: + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + case AbstractTextureFormat::D32F: + return DXGI_FORMAT_R32_FLOAT; + case AbstractTextureFormat::D32F_S8: + return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; + default: + PanicAlert("Unhandled SRV format"); + return DXGI_FORMAT_UNKNOWN; + } +} + +DXGI_FORMAT GetRTVFormatForAbstractFormat(AbstractTextureFormat format, bool integer) +{ + switch (format) + { + case AbstractTextureFormat::RGBA8: + return integer ? DXGI_FORMAT_R8G8B8A8_UINT : DXGI_FORMAT_R8G8B8A8_UNORM; + case AbstractTextureFormat::BGRA8: + return DXGI_FORMAT_B8G8R8A8_UNORM; + case AbstractTextureFormat::R16: + return integer ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R16_UNORM; + case AbstractTextureFormat::R32F: + return DXGI_FORMAT_R32_FLOAT; + default: + PanicAlert("Unhandled RTV format"); + return DXGI_FORMAT_UNKNOWN; + } +} +DXGI_FORMAT GetDSVFormatForAbstractFormat(AbstractTextureFormat format) +{ + switch (format) + { + case AbstractTextureFormat::D16: + return DXGI_FORMAT_D16_UNORM; + case AbstractTextureFormat::D24_S8: + return DXGI_FORMAT_D24_UNORM_S8_UINT; + case AbstractTextureFormat::D32F: + return DXGI_FORMAT_D32_FLOAT; + case AbstractTextureFormat::D32F_S8: + return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; + default: + PanicAlert("Unhandled DSV format"); + return DXGI_FORMAT_UNKNOWN; + } +} + +AbstractTextureFormat GetAbstractFormatForDXGIFormat(DXGI_FORMAT format) +{ + switch (format) + { + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + return AbstractTextureFormat::RGBA8; + + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + return AbstractTextureFormat::BGRA8; + + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_TYPELESS: + return AbstractTextureFormat::R16; + + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_TYPELESS: + return AbstractTextureFormat::R32F; + + case DXGI_FORMAT_D16_UNORM: + return AbstractTextureFormat::D16; + + case DXGI_FORMAT_D24_UNORM_S8_UINT: + return AbstractTextureFormat::D24_S8; + + case DXGI_FORMAT_D32_FLOAT: + return AbstractTextureFormat::D32F; + + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + return AbstractTextureFormat::D32F_S8; + + case DXGI_FORMAT_BC1_UNORM: + return AbstractTextureFormat::DXT1; + case DXGI_FORMAT_BC2_UNORM: + return AbstractTextureFormat::DXT3; + case DXGI_FORMAT_BC3_UNORM: + return AbstractTextureFormat::DXT5; + case DXGI_FORMAT_BC7_UNORM: + return AbstractTextureFormat::BPTC; + + default: + return AbstractTextureFormat::Undefined; + } +} + +void SetDebugObjectName(IUnknown* resource, const char* format, ...) +{ + if (!g_ActiveConfig.bEnableValidationLayer) + return; + + std::va_list ap; + va_start(ap, format); + std::string name = StringFromFormatV(format, ap); + va_end(ap); + + Microsoft::WRL::ComPtr child11; + Microsoft::WRL::ComPtr child12; + if (SUCCEEDED(resource->QueryInterface(IID_PPV_ARGS(&child11)))) + { + child11->SetPrivateData(WKPDID_D3DDebugObjectName, static_cast(name.length()), + name.c_str()); + } + else if (SUCCEEDED(resource->QueryInterface(IID_PPV_ARGS(&child12)))) + { + child12->SetPrivateData(WKPDID_D3DDebugObjectName, static_cast(name.length()), + name.c_str()); + } +} + +std::string GetDebugObjectName(IUnknown* resource) +{ + if (!g_ActiveConfig.bEnableValidationLayer) + return {}; + + std::string name; + UINT size = 0; + + Microsoft::WRL::ComPtr child11; + Microsoft::WRL::ComPtr child12; + if (SUCCEEDED(resource->QueryInterface(IID_PPV_ARGS(&child11)))) + { + child11->GetPrivateData(WKPDID_D3DDebugObjectName, &size, nullptr); + name.resize(size); + child11->GetPrivateData(WKPDID_D3DDebugObjectName, &size, name.data()); + } + else if (SUCCEEDED(resource->QueryInterface(IID_PPV_ARGS(&child12)))) + { + child12->GetPrivateData(WKPDID_D3DDebugObjectName, &size, nullptr); + name.resize(size); + child12->GetPrivateData(WKPDID_D3DDebugObjectName, &size, name.data()); + } + + return name; +} +} // namespace D3DCommon diff --git a/Source/Core/VideoBackends/D3DCommon/Common.h b/Source/Core/VideoBackends/D3DCommon/Common.h new file mode 100644 index 0000000000..3fd5710b05 --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/Common.h @@ -0,0 +1,46 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "VideoCommon/VideoCommon.h" + +struct IDXGIFactory2; + +enum class AbstractTextureFormat : u32; + +namespace D3DCommon +{ +// Loading dxgi.dll and d3dcompiler.dll +bool LoadLibraries(); +void UnloadLibraries(); + +// Returns a list of D3D device names. +std::vector GetAdapterNames(); + +// Helper function which creates a DXGI factory. +IDXGIFactory2* CreateDXGIFactory(bool debug_device); + +// Globally-accessible D3DCompiler function. +extern pD3DCompile d3d_compile; + +// Helpers for texture format conversion. +DXGI_FORMAT GetDXGIFormatForAbstractFormat(AbstractTextureFormat format, bool typeless); +DXGI_FORMAT GetSRVFormatForAbstractFormat(AbstractTextureFormat format); +DXGI_FORMAT GetRTVFormatForAbstractFormat(AbstractTextureFormat format, bool integer); +DXGI_FORMAT GetDSVFormatForAbstractFormat(AbstractTextureFormat format); +AbstractTextureFormat GetAbstractFormatForDXGIFormat(DXGI_FORMAT format); + +// This function will assign a name to the given resource. +// The DirectX debug layer will make it easier to identify resources that way, +// e.g. when listing up all resources who have unreleased references. +void SetDebugObjectName(IUnknown* resource, const char* format, ...); +std::string GetDebugObjectName(IUnknown* resource); +} // namespace D3DCommon diff --git a/Source/Core/VideoBackends/D3DCommon/D3DCommon.vcxproj b/Source/Core/VideoBackends/D3DCommon/D3DCommon.vcxproj new file mode 100644 index 0000000000..7b94ad2b97 --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/D3DCommon.vcxproj @@ -0,0 +1,68 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {DEA96CF2-F237-4A1A-B32F-C916769EFB50} + 10.0.17134.0 + + + + StaticLibrary + v141 + Unicode + + + true + + + false + + + + + + + + + + + + + NotUsing + + + + + + NotUsing + + + + + + + + + + + + + + + + {3de9ee35-3e91-4f27-a014-2866ad8c3fe3} + + + + + + \ No newline at end of file diff --git a/Source/Core/VideoBackends/D3DCommon/D3DCommon.vcxproj.filters b/Source/Core/VideoBackends/D3DCommon/D3DCommon.vcxproj.filters new file mode 100644 index 0000000000..94fb9dd5a9 --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/D3DCommon.vcxproj.filters @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Core/VideoBackends/D3DCommon/Shader.cpp b/Source/Core/VideoBackends/D3DCommon/Shader.cpp new file mode 100644 index 0000000000..d53c5ab2e4 --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/Shader.cpp @@ -0,0 +1,145 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Assert.h" +#include "Common/FileUtil.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" +#include "Common/StringUtil.h" + +#include "VideoBackends/D3DCommon/Shader.h" +#include "VideoCommon/VideoConfig.h" + +namespace D3DCommon +{ +Shader::Shader(ShaderStage stage, BinaryData bytecode) + : AbstractShader(stage), m_bytecode(std::move(bytecode)) +{ +} + +Shader::~Shader() = default; + +bool Shader::HasBinary() const +{ + return true; +} + +AbstractShader::BinaryData Shader::GetBinary() const +{ + return m_bytecode; +} + +static const char* GetCompileTarget(D3D_FEATURE_LEVEL feature_level, ShaderStage stage) +{ + switch (stage) + { + case ShaderStage::Vertex: + { + switch (feature_level) + { + case D3D_FEATURE_LEVEL_10_0: + return "vs_4_0"; + case D3D_FEATURE_LEVEL_10_1: + return "vs_4_1"; + default: + return "vs_5_0"; + } + } + + case ShaderStage::Geometry: + { + switch (feature_level) + { + case D3D_FEATURE_LEVEL_10_0: + return "gs_4_0"; + case D3D_FEATURE_LEVEL_10_1: + return "gs_4_1"; + default: + return "gs_5_0"; + } + } + + case ShaderStage::Pixel: + { + switch (feature_level) + { + case D3D_FEATURE_LEVEL_10_0: + return "ps_4_0"; + case D3D_FEATURE_LEVEL_10_1: + return "ps_4_1"; + default: + return "ps_5_0"; + } + } + + case ShaderStage::Compute: + { + switch (feature_level) + { + case D3D_FEATURE_LEVEL_10_0: + case D3D_FEATURE_LEVEL_10_1: + return ""; + + default: + return "cs_5_0"; + } + } + + default: + return ""; + } +} + +bool Shader::CompileShader(D3D_FEATURE_LEVEL feature_level, BinaryData* out_bytecode, + ShaderStage stage, const char* source, size_t length) +{ + static constexpr D3D_SHADER_MACRO macros[] = {{"API_D3D", "1"}, {nullptr, nullptr}}; + const UINT flags = g_ActiveConfig.bEnableValidationLayer ? + (D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION) : + (D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_SKIP_VALIDATION); + const char* target = GetCompileTarget(feature_level, stage); + + Microsoft::WRL::ComPtr code; + Microsoft::WRL::ComPtr errors; + HRESULT hr = d3d_compile(source, length, nullptr, macros, nullptr, "main", target, flags, 0, + &code, &errors); + if (FAILED(hr)) + { + static int num_failures = 0; + std::string filename = StringFromFormat( + "%sbad_%s_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), target, num_failures++); + std::ofstream file; + File::OpenFStream(file, filename, std::ios_base::out); + file.write(source, length); + file << "\n"; + file.write(static_cast(errors->GetBufferPointer()), errors->GetBufferSize()); + file.close(); + + PanicAlert("Failed to compile %s:\nDebug info (%s):\n%s", filename.c_str(), target, + static_cast(errors->GetBufferPointer())); + return false; + } + + if (errors && errors->GetBufferSize() > 0) + { + WARN_LOG(VIDEO, "%s compilation succeeded with warnings:\n%s", target, + static_cast(errors->GetBufferPointer())); + } + + out_bytecode->resize(code->GetBufferSize()); + std::memcpy(out_bytecode->data(), code->GetBufferPointer(), code->GetBufferSize()); + return true; +} + +AbstractShader::BinaryData Shader::CreateByteCode(const void* data, size_t length) +{ + BinaryData bytecode(length); + std::memcpy(bytecode.data(), data, length); + return bytecode; +} + +} // namespace D3DCommon diff --git a/Source/Core/VideoBackends/D3DCommon/Shader.h b/Source/Core/VideoBackends/D3DCommon/Shader.h new file mode 100644 index 0000000000..6513cd11fe --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/Shader.h @@ -0,0 +1,33 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once +#include +#include "VideoBackends/D3DCommon/Common.h" +#include "VideoCommon/AbstractShader.h" + +namespace D3DCommon +{ +class Shader : public AbstractShader +{ +public: + virtual ~Shader() override; + + const BinaryData& GetByteCode() const { return m_bytecode; } + + bool HasBinary() const override; + BinaryData GetBinary() const override; + + static bool CompileShader(D3D_FEATURE_LEVEL feature_level, BinaryData* out_bytecode, + ShaderStage stage, const char* source, size_t length); + + static BinaryData CreateByteCode(const void* data, size_t length); + +protected: + Shader(ShaderStage stage, BinaryData bytecode); + + BinaryData m_bytecode; +}; + +} // namespace D3DCommon diff --git a/Source/Core/VideoBackends/D3DCommon/SwapChain.cpp b/Source/Core/VideoBackends/D3DCommon/SwapChain.cpp new file mode 100644 index 0000000000..376f891a96 --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/SwapChain.cpp @@ -0,0 +1,232 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoBackends/D3DCommon/SwapChain.h" + +#include +#include + +#include "Common/Assert.h" +#include "Common/CommonFuncs.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" +#include "VideoCommon/VideoConfig.h" + +static bool IsTearingSupported(IDXGIFactory2* dxgi_factory) +{ + Microsoft::WRL::ComPtr factory5; + if (FAILED(dxgi_factory->QueryInterface(IID_PPV_ARGS(&factory5)))) + return false; + + UINT allow_tearing = 0; + return SUCCEEDED(factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, + sizeof(allow_tearing))) && + allow_tearing != 0; +} + +static bool GetFullscreenState(IDXGISwapChain1* swap_chain) +{ + BOOL fs = FALSE; + return SUCCEEDED(swap_chain->GetFullscreenState(&fs, nullptr)) && fs; +} + +namespace D3DCommon +{ +SwapChain::SwapChain(const WindowSystemInfo& wsi, IDXGIFactory2* dxgi_factory, IUnknown* d3d_device) + : m_wsi(wsi), m_dxgi_factory(dxgi_factory), m_d3d_device(d3d_device), + m_allow_tearing_supported(IsTearingSupported(dxgi_factory)) +{ +} + +SwapChain::~SwapChain() +{ + // Can't destroy swap chain while it's fullscreen. + if (m_swap_chain && GetFullscreenState(m_swap_chain.Get())) + m_swap_chain->SetFullscreenState(FALSE, nullptr); +} + +bool SwapChain::WantsStereo() +{ + return g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer; +} + +u32 SwapChain::GetSwapChainFlags() const +{ + // This flag is necessary if we want to use a flip-model swapchain without locking the framerate + return m_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; +} + +bool SwapChain::CreateSwapChain(bool stereo) +{ + RECT client_rc; + if (GetClientRect(static_cast(m_wsi.render_surface), &client_rc)) + { + m_width = client_rc.right - client_rc.left; + m_height = client_rc.bottom - client_rc.top; + } + + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; + swap_chain_desc.Width = m_width; + swap_chain_desc.Height = m_height; + swap_chain_desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT; + swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swap_chain_desc.SampleDesc.Count = 1; + swap_chain_desc.SampleDesc.Quality = 0; + swap_chain_desc.Format = GetDXGIFormatForAbstractFormat(m_texture_format, false); + swap_chain_desc.Scaling = DXGI_SCALING_STRETCH; + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swap_chain_desc.Stereo = stereo; + swap_chain_desc.Flags = GetSwapChainFlags(); + + HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd( + m_d3d_device.Get(), static_cast(m_wsi.render_surface), &swap_chain_desc, nullptr, + nullptr, &m_swap_chain); + if (FAILED(hr)) + { + // Flip-model discard swapchains aren't supported on Windows 8, so here we fall back to + // a sequential swapchain + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + hr = m_dxgi_factory->CreateSwapChainForHwnd(m_d3d_device.Get(), + static_cast(m_wsi.render_surface), + &swap_chain_desc, nullptr, nullptr, &m_swap_chain); + } + + if (FAILED(hr)) + { + // Flip-model swapchains aren't supported on Windows 7, so here we fall back to a legacy + // BitBlt-model swapchain + swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + hr = m_dxgi_factory->CreateSwapChainForHwnd(m_d3d_device.Get(), + static_cast(m_wsi.render_surface), + &swap_chain_desc, nullptr, nullptr, &m_swap_chain); + } + + if (FAILED(hr)) + { + PanicAlert("Failed to create swap chain with HRESULT %08X", hr); + return false; + } + + // We handle fullscreen ourselves. + hr = m_dxgi_factory->MakeWindowAssociation(static_cast(m_wsi.render_surface), + DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER); + if (FAILED(hr)) + WARN_LOG(VIDEO, "MakeWindowAssociation() failed with HRESULT %08X", hr); + + m_stereo = stereo; + if (!CreateSwapChainBuffers()) + { + PanicAlert("Failed to create swap chain buffers"); + DestroySwapChainBuffers(); + m_swap_chain.Reset(); + return false; + } + + return true; +} + +void SwapChain::DestroySwapChain() +{ + DestroySwapChainBuffers(); + + // Can't destroy swap chain while it's fullscreen. + if (m_swap_chain && GetFullscreenState(m_swap_chain.Get())) + m_swap_chain->SetFullscreenState(FALSE, nullptr); + + m_swap_chain.Reset(); +} + +bool SwapChain::ResizeSwapChain() +{ + DestroySwapChainBuffers(); + + HRESULT hr = m_swap_chain->ResizeBuffers(SWAP_CHAIN_BUFFER_COUNT, 0, 0, + GetDXGIFormatForAbstractFormat(m_texture_format, false), + GetSwapChainFlags()); + if (FAILED(hr)) + WARN_LOG(VIDEO, "ResizeBuffers() failed with HRESULT %08X", hr); + + DXGI_SWAP_CHAIN_DESC1 desc; + if (SUCCEEDED(m_swap_chain->GetDesc1(&desc))) + { + m_width = desc.Width; + m_height = desc.Height; + } + + return CreateSwapChainBuffers(); +} + +void SwapChain::SetStereo(bool stereo) +{ + if (m_stereo == stereo) + return; + + DestroySwapChain(); + if (!CreateSwapChain(stereo)) + { + PanicAlert("Failed to switch swap chain stereo mode"); + CreateSwapChain(false); + } +} + +bool SwapChain::GetFullscreen() const +{ + return GetFullscreenState(m_swap_chain.Get()); +} + +void SwapChain::SetFullscreen(bool request) +{ + m_swap_chain->SetFullscreenState(request, nullptr); +} + +bool SwapChain::CheckForFullscreenChange() +{ + if (m_fullscreen_request != m_has_fullscreen) + { + HRESULT hr = m_swap_chain->SetFullscreenState(m_fullscreen_request, nullptr); + if (SUCCEEDED(hr)) + { + m_has_fullscreen = m_fullscreen_request; + return true; + } + } + + const bool new_fullscreen_state = GetFullscreenState(m_swap_chain.Get()); + if (new_fullscreen_state != m_has_fullscreen) + { + m_has_fullscreen = new_fullscreen_state; + m_fullscreen_request = new_fullscreen_state; + return true; + } + + return false; +} + +bool SwapChain::Present() +{ + // When using sync interval 0, it is recommended to always pass the tearing flag when it is + // supported, even when presenting in windowed mode. However, this flag cannot be used if the app + // is in fullscreen mode as a result of calling SetFullscreenState. + UINT present_flags = 0; + if (m_allow_tearing_supported && !g_ActiveConfig.bVSyncActive && !m_has_fullscreen) + present_flags |= DXGI_PRESENT_ALLOW_TEARING; + + HRESULT hr = m_swap_chain->Present(static_cast(g_ActiveConfig.bVSyncActive), present_flags); + if (FAILED(hr)) + { + WARN_LOG(VIDEO, "Swap chain present failed with HRESULT %08X", hr); + return false; + } + + return true; +} + +bool SwapChain::ChangeSurface(void* native_handle) +{ + DestroySwapChain(); + m_wsi.render_surface = native_handle; + return CreateSwapChain(m_stereo); +} + +} // namespace D3DCommon diff --git a/Source/Core/VideoBackends/D3DCommon/SwapChain.h b/Source/Core/VideoBackends/D3DCommon/SwapChain.h new file mode 100644 index 0000000000..b84c1a95c3 --- /dev/null +++ b/Source/Core/VideoBackends/D3DCommon/SwapChain.h @@ -0,0 +1,74 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/WindowSystemInfo.h" +#include "VideoBackends/D3DCommon/Common.h" +#include "VideoCommon/TextureConfig.h" + +namespace D3DCommon +{ +class SwapChain +{ +public: + SwapChain(const WindowSystemInfo& wsi, IDXGIFactory2* dxgi_factory, IUnknown* d3d_device); + virtual ~SwapChain(); + + // Sufficient buffers for triple buffering. + static const u32 SWAP_CHAIN_BUFFER_COUNT = 3; + + // Returns true if the stereo mode is quad-buffering. + static bool WantsStereo(); + + IDXGISwapChain1* GetDXGISwapChain() const { return m_swap_chain.Get(); } + AbstractTextureFormat GetFormat() const { return m_texture_format; } + u32 GetWidth() const { return m_width; } + u32 GetHeight() const { return m_height; } + u32 GetLayers() const { return m_stereo ? 2u : 1u; } + bool IsStereoEnabled() const { return m_stereo; } + bool HasExclusiveFullscreen() const { return m_has_fullscreen; } + + // Mode switches. + bool GetFullscreen() const; + void SetFullscreen(bool request); + + // Checks for loss of exclusive fullscreen. + bool CheckForFullscreenChange(); + + // Presents the swap chain to the screen. + virtual bool Present(); + + bool ChangeSurface(void* native_handle); + bool ResizeSwapChain(); + void SetStereo(bool stereo); + +protected: + u32 GetSwapChainFlags() const; + bool CreateSwapChain(bool stereo); + void DestroySwapChain(); + + virtual bool CreateSwapChainBuffers() = 0; + virtual void DestroySwapChainBuffers() = 0; + + WindowSystemInfo m_wsi; + Microsoft::WRL::ComPtr m_dxgi_factory; + Microsoft::WRL::ComPtr m_swap_chain; + Microsoft::WRL::ComPtr m_d3d_device; + AbstractTextureFormat m_texture_format = AbstractTextureFormat::RGBA8; + + u32 m_width = 1; + u32 m_height = 1; + + bool m_stereo = false; + bool m_allow_tearing_supported = false; + bool m_has_fullscreen = false; + bool m_fullscreen_request = false; +}; + +} // namespace D3DCommon diff --git a/Source/dolphin-emu.sln b/Source/dolphin-emu.sln index 20b4d9d146..0e4ef7ca99 100644 --- a/Source/dolphin-emu.sln +++ b/Source/dolphin-emu.sln @@ -93,6 +93,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "..\Externals\imgui EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdaterCommon", "Core\UpdaterCommon\UpdaterCommon.vcxproj", "{B001D13E-7EAB-4689-842D-801E5ACFFAC5}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3DCommon", "Core\VideoBackends\D3DCommon\D3DCommon.vcxproj", "{DEA96CF2-F237-4A1A-B32F-C916769EFB50}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -353,6 +355,12 @@ Global {B001D13E-7EAB-4689-842D-801E5ACFFAC5}.Release|x64.ActiveCfg = Release|x64 {B001D13E-7EAB-4689-842D-801E5ACFFAC5}.Release|x64.Build.0 = Release|x64 {B001D13E-7EAB-4689-842D-801E5ACFFAC5}.Release|x86.ActiveCfg = Release|x64 + {DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Debug|x64.ActiveCfg = Debug|x64 + {DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Debug|x64.Build.0 = Debug|x64 + {DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Debug|x86.ActiveCfg = Debug|x64 + {DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Release|x64.ActiveCfg = Release|x64 + {DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Release|x64.Build.0 = Release|x64 + {DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Release|x86.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -394,6 +402,7 @@ Global {4482FD2A-EC43-3FFB-AC20-2E5C54B05EAD} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {23114507-079A-4418-9707-CFA81A03CA99} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {4C3B2264-EA73-4A7B-9CFE-65B0FD635EBB} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} + {DEA96CF2-F237-4A1A-B32F-C916769EFB50} = {AAD1BCD6-9804-44A5-A5FC-4782EA00E9D4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {710976F2-1BC7-4F2A-B32D-5DD2BBCB44E1}