Add D3DCommon (shared code between D3D11 and D3D12)

This commit is contained in:
Stenzek 2019-03-09 23:31:35 +10:00
parent f6641b7e4f
commit ea15080d8f
11 changed files with 956 additions and 0 deletions

View File

@ -4,6 +4,7 @@ add_subdirectory(Software)
add_subdirectory(Vulkan)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_subdirectory(D3DCommon)
add_subdirectory(D3D)
endif()

View File

@ -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
)

View File

@ -0,0 +1,320 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <d3d11.h>
#include <d3d12.h>
#include <dxgi1_3.h>
#include <wrl/client.h>
#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<std::string> GetAdapterNames()
{
Microsoft::WRL::ComPtr<IDXGIFactory> factory;
HRESULT hr = create_dxgi_factory(IID_PPV_ARGS(&factory));
if (!SUCCEEDED(hr))
return {};
std::vector<std::string> adapters;
IDXGIAdapter* adapter;
while (factory->EnumAdapters(static_cast<UINT>(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<ID3D11DeviceChild> child11;
Microsoft::WRL::ComPtr<ID3D12DeviceChild> child12;
if (SUCCEEDED(resource->QueryInterface(IID_PPV_ARGS(&child11))))
{
child11->SetPrivateData(WKPDID_D3DDebugObjectName, static_cast<UINT>(name.length()),
name.c_str());
}
else if (SUCCEEDED(resource->QueryInterface(IID_PPV_ARGS(&child12))))
{
child12->SetPrivateData(WKPDID_D3DDebugObjectName, static_cast<UINT>(name.length()),
name.c_str());
}
}
std::string GetDebugObjectName(IUnknown* resource)
{
if (!g_ActiveConfig.bEnableValidationLayer)
return {};
std::string name;
UINT size = 0;
Microsoft::WRL::ComPtr<ID3D11DeviceChild> child11;
Microsoft::WRL::ComPtr<ID3D12DeviceChild> 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

View File

@ -0,0 +1,46 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <d3dcompiler.h>
#include <dxgiformat.h>
#include <string>
#include <vector>
#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<std::string> 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

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{DEA96CF2-F237-4A1A-B32F-C916769EFB50}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\VSProps\Base.props" />
<Import Project="..\..\..\VSProps\PCHUse.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles />
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles />
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Common.cpp" />
<ClCompile Include="Shader.cpp" />
<ClCompile Include="SwapChain.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Common.h" />
<ClInclude Include="Shader.h" />
<ClInclude Include="SwapChain.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
<Project>{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="SwapChain.cpp" />
<ClCompile Include="Common.cpp" />
<ClCompile Include="Shader.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="SwapChain.h" />
<ClInclude Include="Common.h" />
<ClInclude Include="Shader.h" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,145 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <fstream>
#include <wrl/client.h>
#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<ID3DBlob> code;
Microsoft::WRL::ComPtr<ID3DBlob> 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<const char*>(errors->GetBufferPointer()), errors->GetBufferSize());
file.close();
PanicAlert("Failed to compile %s:\nDebug info (%s):\n%s", filename.c_str(), target,
static_cast<const char*>(errors->GetBufferPointer()));
return false;
}
if (errors && errors->GetBufferSize() > 0)
{
WARN_LOG(VIDEO, "%s compilation succeeded with warnings:\n%s", target,
static_cast<const char*>(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

View File

@ -0,0 +1,33 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <memory>
#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

View File

@ -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 <algorithm>
#include <cstdint>
#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<IDXGIFactory5> 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<HWND>(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<HWND>(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<HWND>(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<HWND>(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<HWND>(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<UINT>(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

View File

@ -0,0 +1,74 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <dxgi1_5.h>
#include <wrl/client.h>
#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<IDXGIFactory2> m_dxgi_factory;
Microsoft::WRL::ComPtr<IDXGISwapChain1> m_swap_chain;
Microsoft::WRL::ComPtr<IUnknown> 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

View File

@ -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}