| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // Copyright 2019 Dolphin Emulator Project | ||
| // Licensed under GPLv2+ | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #include "VideoBackends/D3D/SwapChain.h" | ||
| #include "VideoBackends/D3D/DXTexture.h" | ||
|
|
||
| namespace DX11 | ||
| { | ||
| SwapChain::SwapChain(const WindowSystemInfo& wsi, IDXGIFactory2* dxgi_factory, | ||
| ID3D11Device* d3d_device) | ||
| : D3DCommon::SwapChain(wsi, dxgi_factory, d3d_device) | ||
| { | ||
| } | ||
|
|
||
| SwapChain::~SwapChain() = default; | ||
|
|
||
| std::unique_ptr<SwapChain> SwapChain::Create(const WindowSystemInfo& wsi) | ||
| { | ||
| std::unique_ptr<SwapChain> swap_chain = | ||
| std::make_unique<SwapChain>(wsi, D3D::dxgi_factory.Get(), D3D::device.Get()); | ||
| if (!swap_chain->CreateSwapChain(WantsStereo())) | ||
| return nullptr; | ||
|
|
||
| return swap_chain; | ||
| } | ||
|
|
||
| bool SwapChain::CreateSwapChainBuffers() | ||
| { | ||
| ComPtr<ID3D11Texture2D> texture; | ||
| HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(&texture)); | ||
| CHECK(SUCCEEDED(hr), "Get swap chain buffer"); | ||
| if (FAILED(hr)) | ||
| return false; | ||
|
|
||
| m_texture = DXTexture::CreateAdopted(texture.Get()); | ||
| if (!m_texture) | ||
| return false; | ||
|
|
||
| m_framebuffer = DXFramebuffer::Create(m_texture.get(), nullptr); | ||
| if (!m_framebuffer) | ||
| return false; | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void SwapChain::DestroySwapChainBuffers() | ||
| { | ||
| m_framebuffer.reset(); | ||
| m_texture.reset(); | ||
| } | ||
| } // namespace DX11 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Copyright 2019 Dolphin Emulator Project | ||
| // Licensed under GPLv2+ | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <d3d11.h> | ||
| #include <dxgi.h> | ||
| #include <memory> | ||
| #include <vector> | ||
|
|
||
| #include "Common/CommonTypes.h" | ||
| #include "Common/WindowSystemInfo.h" | ||
| #include "VideoBackends/D3D/D3DBase.h" | ||
| #include "VideoBackends/D3DCommon/SwapChain.h" | ||
| #include "VideoCommon/TextureConfig.h" | ||
|
|
||
| namespace DX11 | ||
| { | ||
| class DXTexture; | ||
| class DXFramebuffer; | ||
|
|
||
| class SwapChain : public D3DCommon::SwapChain | ||
| { | ||
| public: | ||
| SwapChain(const WindowSystemInfo& wsi, IDXGIFactory2* dxgi_factory, ID3D11Device* d3d_device); | ||
| ~SwapChain(); | ||
|
|
||
| static std::unique_ptr<SwapChain> Create(const WindowSystemInfo& wsi); | ||
|
|
||
| DXTexture* GetTexture() const { return m_texture.get(); } | ||
| DXFramebuffer* GetFramebuffer() const { return m_framebuffer.get(); } | ||
|
|
||
| protected: | ||
| bool CreateSwapChainBuffers() override; | ||
| void DestroySwapChainBuffers() override; | ||
|
|
||
| private: | ||
| // The runtime takes care of renaming the buffers. | ||
| std::unique_ptr<DXTexture> m_texture; | ||
| std::unique_ptr<DXFramebuffer> m_framebuffer; | ||
| }; | ||
|
|
||
| } // namespace DX11 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |